diff --git a/build.xml b/build.xml index 2b4989e597f2b98868c8c16d533547f72b194a92..a430867363dfacede2ee55be60f7af9389a72113 100644 --- a/build.xml +++ b/build.xml @@ -53,13 +53,14 @@ <patternset id="lib"> <include name="logback-classic-1.0.0.jar"/> <include name="logback-core-1.0.0.jar"/> - <include name="jackson-core-2.1.4.jar"/> - <include name="jackson-annotations-2.1.4.jar"/> - <include name="jackson-databind-2.1.4.jar"/> - <include name="jackson-dataformat-smile-2.1.4.jar"/> - <include name="jackson-dataformat-xml-2.1.4.jar"/> - <include name="jackson-dataformat-yaml-2.1.4.jar"/> - <include name="jackson-dataformat-csv-2.1.4.jar"/> + <include name="jackson-core-2.4.4.jar"/> + <include name="jackson-annotations-2.4.4.jar"/> + <include name="jackson-databind-2.4.4.jar"/> + <include name="jackson-dataformat-smile-2.4.4.jar"/> + <include name="jackson-dataformat-xml-2.4.4.jar"/> + <include name="jackson-dataformat-yaml-2.4.4.jar"/> + <include name="jackson-dataformat-csv-2.4.4.jar"/> + <include name="jackson-dataformat-cbor-2.4.4.jar"/> <include name="slf4j-api-1.6.4.jar"/> <include name="org.restlet.jar"/> <include name="org.restlet.ext.jackson.jar"/> @@ -213,7 +214,10 @@ <include name="**/*Test*.java"/> <exclude name="**/core/test/**"/> <exclude name="**/core/module/**"/> - </fileset> + <exclude name="**/util/FutureTestUtils.java"/> <!-- No tests here. Used within others that have Futures --> + <exclude name="**/test/FloodlightTestCase.java"/> <!-- Base class for other tests. Can't run it individually. --> + <exclude name="**/core/internal/OFSwitchHandlerTestBase.java"/> <!-- Abstract class used by protocol version handshake handlers. --> + </fileset> </batchtest> </junit> <fail if="junit.failure" message="Unit test(s) failed. See reports!"/> diff --git a/lib/jackson-annotations-2.1.4.jar b/lib/jackson-annotations-2.1.4.jar deleted file mode 100644 index 143edf44b0daa4cef1a452ecccac21aee22a8d77..0000000000000000000000000000000000000000 Binary files a/lib/jackson-annotations-2.1.4.jar and /dev/null differ diff --git a/lib/jackson-annotations-2.4.4.jar b/lib/jackson-annotations-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..4779188a9f0f306169d566bf7e73570e0bf3e45b Binary files /dev/null and b/lib/jackson-annotations-2.4.4.jar differ diff --git a/lib/jackson-core-2.1.4.jar b/lib/jackson-core-2.1.4.jar deleted file mode 100644 index 0f144685f7140d2694eeba5609322b4cd79f0bf8..0000000000000000000000000000000000000000 Binary files a/lib/jackson-core-2.1.4.jar and /dev/null differ diff --git a/lib/jackson-core-2.4.4.jar b/lib/jackson-core-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..da2a877f186a999df8fbafaac5c59316d3fb70b4 Binary files /dev/null and b/lib/jackson-core-2.4.4.jar differ diff --git a/lib/jackson-databind-2.1.4.jar b/lib/jackson-databind-2.1.4.jar deleted file mode 100644 index ce125d1df292a1d8fa4b86cf39770d0d9c4636cc..0000000000000000000000000000000000000000 Binary files a/lib/jackson-databind-2.1.4.jar and /dev/null differ diff --git a/lib/jackson-databind-2.4.4.jar b/lib/jackson-databind-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..e945e5e800666e0c5f99e9571185327f663d73ce Binary files /dev/null and b/lib/jackson-databind-2.4.4.jar differ diff --git a/lib/jackson-dataformat-cbor-2.4.4.jar b/lib/jackson-dataformat-cbor-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..8a536d2b806a5a50473e602a932f1a2527b3a604 Binary files /dev/null and b/lib/jackson-dataformat-cbor-2.4.4.jar differ diff --git a/lib/jackson-dataformat-csv-2.1.4.jar b/lib/jackson-dataformat-csv-2.1.4.jar deleted file mode 100644 index e97503cb161c44ca54d096ec669b707e522f0004..0000000000000000000000000000000000000000 Binary files a/lib/jackson-dataformat-csv-2.1.4.jar and /dev/null differ diff --git a/lib/jackson-dataformat-csv-2.4.4.jar b/lib/jackson-dataformat-csv-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..f561dc10ff44e60210f04315f508bf76d1843d1c Binary files /dev/null and b/lib/jackson-dataformat-csv-2.4.4.jar differ diff --git a/lib/jackson-dataformat-smile-2.1.4.jar b/lib/jackson-dataformat-smile-2.1.4.jar deleted file mode 100644 index e907c7726c5679a572e098293581bba28ac63fa1..0000000000000000000000000000000000000000 Binary files a/lib/jackson-dataformat-smile-2.1.4.jar and /dev/null differ diff --git a/lib/jackson-dataformat-smile-2.4.4.jar b/lib/jackson-dataformat-smile-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..2a6b285190944cd6536619e0caca6aca4af18067 Binary files /dev/null and b/lib/jackson-dataformat-smile-2.4.4.jar differ diff --git a/lib/jackson-dataformat-xml-2.1.4.jar b/lib/jackson-dataformat-xml-2.1.4.jar deleted file mode 100644 index 15c82e69c86703363e16d47cb8cb3c3de1919c43..0000000000000000000000000000000000000000 Binary files a/lib/jackson-dataformat-xml-2.1.4.jar and /dev/null differ diff --git a/lib/jackson-dataformat-xml-2.4.4.jar b/lib/jackson-dataformat-xml-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..8f0fa34d09634e02bda7dc0fe7938b8e58fdb1a2 Binary files /dev/null and b/lib/jackson-dataformat-xml-2.4.4.jar differ diff --git a/lib/jackson-dataformat-yaml-2.1.4.jar b/lib/jackson-dataformat-yaml-2.1.4.jar deleted file mode 100644 index 444ef88653e9574fb90246679835448e02170ee8..0000000000000000000000000000000000000000 Binary files a/lib/jackson-dataformat-yaml-2.1.4.jar and /dev/null differ diff --git a/lib/jackson-dataformat-yaml-2.4.4.jar b/lib/jackson-dataformat-yaml-2.4.4.jar new file mode 100644 index 0000000000000000000000000000000000000000..7770cb918467a8141c8d169f09042a5112e5f36d Binary files /dev/null and b/lib/jackson-dataformat-yaml-2.4.4.jar differ diff --git a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar b/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar index 336684ee97dcfdb417dff49df9600548498c7163..3b2cd87c8da1a2ba622894c2241b4321b59e6ce2 100644 Binary files a/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar and b/lib/openflowj-0.9.0-SNAPSHOT-javadoc.jar differ diff --git a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar b/lib/openflowj-0.9.0-SNAPSHOT-sources.jar index a4086ecfd7d6602661b35f1480d95cf0a0554739..abdf436f91750e82a1e50f3376105bf590526477 100644 Binary files a/lib/openflowj-0.9.0-SNAPSHOT-sources.jar and b/lib/openflowj-0.9.0-SNAPSHOT-sources.jar differ diff --git a/lib/openflowj-0.9.0-SNAPSHOT.jar b/lib/openflowj-0.9.0-SNAPSHOT.jar index 929c5bdbf4191b8ffb2c3dd042d882e021bd6c70..2a2140a0038065ee1bc875723a42ae9b9cf8ece0 100644 Binary files a/lib/openflowj-0.9.0-SNAPSHOT.jar and b/lib/openflowj-0.9.0-SNAPSHOT.jar differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..05c0b1784d45c95844c78a6f0f48256f030475db --- /dev/null +++ b/pom.xml @@ -0,0 +1,233 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.projectfloodlight</groupId> + <artifactId>floodlight</artifactId> + <version>1.2-SNAPSHOT</version> + <name>floodlight</name> + <repositories> + <repository> + <id>maven-restlet</id> + <name>Public online Restlet repository</name> + <url>http://maven.restlet.com</url> + </repository> + <repository> + <id>sonatype-oss-snapshot</id> + <name>Sonatype OSS snapshot repository</name> + <url>https://oss.sonatype.org/content/repositories/snapshots</url> + </repository> + </repositories> + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.0</version> + <configuration> + <source>1.7</source> + <target>1.7</target> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.7</version> + <executions> + <execution> + <id>add-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>lib/gen-java</source> + <source>src/main/python</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + <pluginManagement> + <plugins> + <!--This plugin's configuration is used to store Eclipse m2e settings + only. It has no influence on the Maven build itself. --> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId> + org.codehaus.mojo + </groupId> + <artifactId> + build-helper-maven-plugin + </artifactId> + <versionRange> + [1.7,) + </versionRange> + <goals> + <goal>add-source</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + <resources> + <resource> + <directory>${basedir}</directory> + <includes> + <include>LICENSE.txt</include> + <include>NOTICE.txt</include> + <include>README.md</include> + </includes> + </resource> + </resources> + </build> + <dependencies> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>13.0.1</version> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.0.0</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.6.4</version> + </dependency> + <dependency> + <groupId>org.restlet.jse</groupId> + <artifactId>org.restlet</artifactId> + <version>${lib-restlet-version}</version> + </dependency> + <dependency> + <groupId>org.restlet.jse</groupId> + <artifactId>org.restlet.ext.jackson</artifactId> + <version>${lib-restlet-version}</version> + </dependency> + <dependency> + <groupId>org.restlet.jse</groupId> + <artifactId>org.restlet.ext.simple</artifactId> + <version>${lib-restlet-version}</version> + </dependency> + <dependency> + <groupId>org.restlet.jse</groupId> + <artifactId>org.restlet.ext.slf4j</artifactId> + <version>${lib-restlet-version}</version> + </dependency> + <dependency> + <groupId>org.restlet.jse</groupId> + <artifactId>org.restlet.ext.jsslutils</artifactId> + <version>${lib-restlet-version}</version> + </dependency> + <dependency> + <groupId>args4j</groupId> + <artifactId>args4j</artifactId> + <version>2.0.16</version> + </dependency> + <dependency> + <groupId>com.googlecode.concurrentlinkedhashmap</groupId> + <artifactId>concurrentlinkedhashmap-lru</artifactId> + <version>1.2</version> + </dependency> + <dependency> + <groupId>org.python</groupId> + <artifactId>jython-standalone</artifactId> + <version>2.5.2</version> + </dependency> + <dependency> + <groupId>org.apache.thrift</groupId> + <artifactId>libthrift</artifactId> + <version>0.9.0</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.8.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>3.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>cglib</groupId> + <artifactId>cglib-nodep</artifactId> + <version>2.2.2</version> + </dependency> + <dependency> + <groupId>org.objenesis</groupId> + <artifactId>objenesis</artifactId> + <version>1.2</version> + </dependency> + <dependency> + <groupId>org.apache.derby</groupId> + <artifactId>derby</artifactId> + <version>10.9.1.0</version> + </dependency> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>jsr305</artifactId> + <version>2.0.1</version> + </dependency> + <dependency> + <groupId>com.google.code.findbugs</groupId> + <artifactId>annotations</artifactId> + <version>2.0.1</version> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + <version>${lib-hamcrest-version}</version> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-integration</artifactId> + <version>${lib-hamcrest-version}</version> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-library</artifactId> + <version>${lib-hamcrest-version}</version> + </dependency> + <dependency> + <groupId>org.projectfloodlight</groupId> + <artifactId>openflowj</artifactId> + <version>0.9.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + <version>3.9.0.Final</version> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-cbor</artifactId> + <version>${lib-jackson-version}</version> + </dependency> + </dependencies> + <properties> + <lib-restlet-version>2.3.2</lib-restlet-version> + <lib-jackson-version>2.4.4</lib-jackson-version> + <lib-hamcrest-version>1.3</lib-hamcrest-version> + </properties> +</project> diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java index 36b13123c233c2728ec87083fb608d651bd261f5..edfc1015f656cde20bf9589091547fd582ed451e 100644 --- a/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java +++ b/src/main/java/net/floodlightcontroller/accesscontrollist/ACL.java @@ -24,10 +24,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import net.floodlightcontroller.accesscontrollist.ACLRule.Action; import net.floodlightcontroller.accesscontrollist.ap.AP; @@ -54,226 +54,254 @@ import org.slf4j.LoggerFactory; public class ACL implements IACLService, IFloodlightModule, IDeviceListener { - // service modules needed protected IRestApiService restApi; protected IDeviceService deviceManager; protected IStorageSourceService storageSource; protected static Logger logger; - - private APManager apManager; - // variable used + private APManager apManager; private int lastRuleId = 1; // rule id counter - private List<ACLRule> ruleSet; + private Map<Integer, ACLRule> aclRules; private Map<String, Integer> dpid2FlowPriority; private Map<Integer, Set<String>> ruleId2Dpid; private Map<Integer, Set<String>> ruleId2FlowName; + private Map<Integer, List<Integer>> deny2Allow; + + private final int DEFAULT_PRIORITY = 30000; /** - * used by REST API to query ACL rules + * Checks if an existing ACL rule already works in a given switch. */ + private boolean checkIfRuleWorksInSwitch(int ruleId, String dpid) { + return ruleId2Dpid.containsKey(ruleId) + && ruleId2Dpid.get(ruleId).contains(dpid); + } + + /** + * Adds a new mapping from ACL rule to ACL flow. + */ + private void addRuleToFlowMapping(int ruleId, String flowName) { + if (!ruleId2FlowName.containsKey(ruleId)) { + ruleId2FlowName.put(ruleId, new HashSet<String>()); + } + ruleId2FlowName.get(ruleId).add(flowName); + } + + /** + * Adds a new mapping from ACL rule to switch. + */ + private void addRuleToSwitchMapping(int ruleId, String dpid) { + if (!ruleId2Dpid.containsKey(ruleId)) { + ruleId2Dpid.put(ruleId, new HashSet<String>()); + } + ruleId2Dpid.get(ruleId).add(dpid); + } + + /** + * Gets the current priority for new ACL flow by device id. + */ + private int getPriorityBySwitch(String dpid) { + if (!dpid2FlowPriority.containsKey(dpid)) { + dpid2FlowPriority.put(dpid, DEFAULT_PRIORITY - 1); + return DEFAULT_PRIORITY; + } else { + int priority = dpid2FlowPriority.get(dpid); + dpid2FlowPriority.put(dpid, priority - 1); + return priority; + } + } + @Override public List<ACLRule> getRules() { - return this.ruleSet; + return new ArrayList<ACLRule>(aclRules.values()); } /** - * check if the new rule matches an existing rule + * Checks if the new ACL rule matches an existing rule. If existing allowing + * rules matches the new denying rule, store the mappings. + * + * @return true if the new ACL rule matches an existing rule, false + * otherwise */ private boolean checkRuleMatch(ACLRule newRule) { - Iterator<ACLRule> iter = ruleSet.iterator(); - while (iter.hasNext()) { - ACLRule existingRule = iter.next(); - if(newRule.match(existingRule)){ - logger.error("existing rule: " + existingRule); + List<Integer> allowRuleList = new ArrayList<>(); + for (ACLRule existingRule : getRules()) { + if (newRule.match(existingRule)) { return true; } + + if (existingRule.getAction() == Action.ALLOW + && newRule.getAction() == Action.DENY) { + if (existingRule.match(newRule)) { + allowRuleList.add(existingRule.getId()); + } + } } + deny2Allow.put(newRule.getId(), allowRuleList); return false; } - - /** - * used by REST API to add ACL rule - * @return if the new ACL rule is added successfully - */ + @Override public boolean addRule(ACLRule rule) { - - if(checkRuleMatch(rule)){ + rule.setId(lastRuleId++); + if (checkRuleMatch(rule)) { + lastRuleId--; return false; } - - rule.setId(lastRuleId++); - this.ruleSet.add(rule); - logger.info("No.{} ACL rule added.", rule.getId()); - enforceAddedRule(rule); + aclRules.put(rule.getId(), rule); + logger.info("ACL rule(id:{}) is added.", rule.getId()); + if (rule.getAction() != Action.ALLOW) { + enforceAddedRule(rule); + } return true; } - /** - * used by REST API to remove ACL rule - */ @Override - public void removeRule(int ruleid) { - - Iterator<ACLRule> iter = this.ruleSet.iterator(); - while (iter.hasNext()) { - ACLRule rule = iter.next(); - if (rule.getId() == ruleid) { iter.remove(); - break; - } - } - - logger.info("No.{} ACL rule removed.", ruleid); - enforceRemovedRule(ruleid); + public void removeRule(int ruleId) { + aclRules.remove(ruleId); + logger.info("ACL rule(id:{}) is removed.", ruleId); + enforceRemovedRule(ruleId); } - /** - * used by REST API to clear ACL - */ @Override public void removeAllRules() { - this.lastRuleId = 1; - this.ruleSet = new ArrayList<ACLRule>(); - this.dpid2FlowPriority = new HashMap<String, Integer>(); - this.ruleId2Dpid = new HashMap<Integer, Set<String>>(); - - Iterator<Integer> ruleIdIter = ruleId2FlowName.keySet().iterator(); - while (ruleIdIter.hasNext()) { - int ruleId = ruleIdIter.next(); - Set<String> flowNameSet = ruleId2FlowName.get(ruleId); - logger.info("No.{} ACL rule removed.", ruleId); + this.aclRules = new TreeMap<>(); + this.dpid2FlowPriority = new HashMap<>(); + this.ruleId2Dpid = new HashMap<>(); + this.deny2Allow = new HashMap<>(); + + for (Set<String> flowNameSet : ruleId2FlowName.values()) { for (String flowName : flowNameSet) { - removeFlow(flowName); - logger.info("ACL flow {} removed.", flowName); + storageSource.deleteRowAsync("controller_staticflowtableentry", + flowName); + logger.debug("ACL flow(id:{}) is removed.", flowName); } } - this.ruleId2FlowName = new HashMap<Integer, Set<String>>(); + this.ruleId2FlowName = new HashMap<>(); } /** - * enforce new added rule + * Enforces denying ACL rule by ACL flow. */ - private void enforceAddedRule(ACLRule rule) { - + private void enforceAddedRule(ACLRule denyRule) { Set<String> dpidSet; - if (rule.getNw_src() != null) { - dpidSet = apManager.getDpidSet(rule.getNw_src_prefix(),rule.getNw_src_maskbits()); + if (denyRule.getNw_src() != null) { + dpidSet = apManager.getDpidSet(denyRule.getNw_src_prefix(), + denyRule.getNw_src_maskbits()); } else { - dpidSet = apManager.getDpidSet(rule.getNw_dst_prefix(),rule.getNw_dst_maskbits()); + dpidSet = apManager.getDpidSet(denyRule.getNw_dst_prefix(), + denyRule.getNw_dst_maskbits()); } - Iterator<String> dpidIter = dpidSet.iterator(); - Set<String> nameSet = new HashSet<String>(); - - while (dpidIter.hasNext()) { - String dpid = dpidIter.next(); - String flowName = "ACLRule_" + rule.getId() + "_" + dpid; - generateFlow(rule, dpid, flowName); - nameSet.add(flowName); + for (String dpid : dpidSet) { + String flowName; + List<Integer> allowRuleList = deny2Allow.get(denyRule.getId()); + for (int allowRuleId : allowRuleList) { + flowName = "ACLRule_" + allowRuleId + "_" + dpid; + generateFlow(aclRules.get(allowRuleId), dpid, flowName); + } + flowName = "ACLRule_" + denyRule.getId() + "_" + dpid; + generateFlow(denyRule, dpid, flowName); } - ruleId2FlowName.put(rule.getId(), nameSet); - ruleId2Dpid.put(rule.getId(), dpidSet); } /** - * enforce removed rule + * Enforces removing an existing ACL rule. */ private void enforceRemovedRule(int ruleId) { - - Set<String> flowEntryName = ruleId2FlowName.get(ruleId); - Iterator<String> iter = flowEntryName.iterator(); - while (iter.hasNext()) { - String name = iter.next(); - removeFlow(name); - logger.info("ACL flow " + name + " removed."); + if (ruleId2FlowName.containsKey(ruleId)) { + for (String flowName : ruleId2FlowName.get(ruleId)) { + storageSource.deleteRowAsync("controller_staticflowtableentry", + flowName); + logger.debug("ACL flow(id:{}) is removed.", flowName); + } + ruleId2FlowName.remove(ruleId); } - + ruleId2Dpid.remove(ruleId); + deny2Allow.remove(ruleId); } - + /** - * generate and push ACL flow entry + * Generates ACL flow rule according to ACL rule + * and installs it into switch. */ private void generateFlow(ACLRule rule, String dpid, String flowName) { - - int priority; - // get priority for the new flow entry - if (dpid2FlowPriority.get(dpid) == null) { - dpid2FlowPriority.put(dpid, 30000); - priority = 30000; - } else { - priority = dpid2FlowPriority.get(dpid); + if (rule == null || checkIfRuleWorksInSwitch(rule.getId(), dpid)) { + return; } + int priority = getPriorityBySwitch(dpid); if (rule.getNw_src() != null) { - - HashMap<String,Object> flow = new HashMap<String,Object>(); - + + HashMap<String, Object> flow = new HashMap<String, Object>(); + flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid); flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName); - flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true)); + flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, + Boolean.toString(true)); flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0"); - flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(priority)); - dpid2FlowPriority.put(dpid, --priority); - - + flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, + Integer.toString(priority)); + flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048"); flow.put(StaticFlowEntryPusher.COLUMN_NW_SRC, rule.getNw_src()); - - // process for the nw_dst attribute + if (rule.getNw_dst() != null) { flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst()); } if (rule.getNw_proto() != 0) { - flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Integer.toString(rule.getNw_proto())); + flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, + Integer.toString(rule.getNw_proto())); } if (rule.getAction() == Action.ALLOW) { - flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller"); + flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, + "output=controller"); } if (rule.getTp_dst() != 0) { - flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst())); + flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, + Integer.toString(rule.getTp_dst())); } - - storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); - + + storageSource + .insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); + } else { - - HashMap<String,Object> flow = new HashMap<String,Object>(); - + + HashMap<String, Object> flow = new HashMap<String, Object>(); + flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, dpid); flow.put(StaticFlowEntryPusher.COLUMN_NAME, flowName); - flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true)); + flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, + Boolean.toString(true)); flow.put(StaticFlowEntryPusher.COLUMN_COOKIE, "0"); - flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, Integer.toString(priority)); - dpid2FlowPriority.put(dpid, --priority); - + flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, + Integer.toString(priority)); + flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048"); flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, rule.getNw_dst()); if (rule.getNw_proto() != 0) { - flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Integer.toString(rule.getNw_proto())); + flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, + Integer.toString(rule.getNw_proto())); } if (rule.getAction() == Action.ALLOW) { - flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller"); + flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, + "output=controller"); } if (rule.getTp_dst() != 0) { - flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, Integer.toString(rule.getTp_dst())); + flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, + Integer.toString(rule.getTp_dst())); } - - storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); - - } - logger.info("ACL flow " + flowName + " added in " + dpid); - } - /** - * remove ACL flow entry - */ - private void removeFlow(String name) { - storageSource.deleteRowAsync("controller_staticflowtableentry", name); - } + storageSource + .insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); + } + addRuleToSwitchMapping(rule.getId(), dpid); + addRuleToFlowMapping(rule.getId(), flowName); + logger.debug("ACL flow(id:{}) is added in {}.", flowName, dpid); + } @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { @@ -306,11 +334,12 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { logger = LoggerFactory.getLogger(ACL.class); storageSource = context.getServiceImpl(IStorageSourceService.class); - ruleSet = new ArrayList<ACLRule>(); + aclRules = new TreeMap<>(); apManager = new APManager(); - ruleId2FlowName = new HashMap<Integer, Set<String>>(); - ruleId2Dpid = new HashMap<Integer, Set<String>>(); - dpid2FlowPriority = new HashMap<String, Integer>(); + ruleId2FlowName = new HashMap<>(); + ruleId2Dpid = new HashMap<>(); + dpid2FlowPriority = new HashMap<>(); + deny2Allow = new HashMap<>(); } @Override @@ -320,60 +349,55 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { deviceManager.addListener(this); } - /** - * listen for new device - */ @Override public void deviceAdded(IDevice device) { SwitchPort[] switchPort = device.getAttachmentPoints(); IPv4Address[] ips = device.getIPv4Addresses(); - if(ips.length == 0){ + if (ips.length == 0) { // A new no-ip device added return; } - String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong()); + + String dpid = HexString.toHexString(switchPort[0].getSwitchDPID() + .getLong()); String ip = IPv4.fromIPv4Address(ips[0].getInt()); - logger.info("New AP added. [dpid:" + dpid + " ip:" + ip + "]"); + logger.debug("AP(dpid:{},ip:{}) is added", dpid, ip); - AP ap = new AP(ip,dpid); + AP ap = new AP(ip, dpid); apManager.addAP(ap); processAPAdded(ap); } /** - * push ACL flow given the new device + * Generates new ACL flow when a new device appears + * and existing ACL rules denies its traffic. */ private void processAPAdded(AP ap) { - String dpid = ap.getDpid(); int ip = IPv4.toIPv4Address(ap.getIp()); - Iterator<ACLRule> iter = this.ruleSet.iterator(); - while (iter.hasNext()) { - ACLRule rule = iter.next(); - if (rule.getNw_src() != null) { - if (IPAddressUtil.containIP(rule.getNw_src_prefix(), - rule.getNw_src_maskbits(), ip)) { - // check if there is a flow entry in the switch for the rule - if (ruleId2Dpid.get(rule.getId()).contains(dpid)) { - continue; + for (ACLRule rule : getRules()) { + if (rule.getAction() != Action.ALLOW) { + if (rule.getNw_src() != null) { + if (IPAddressUtil.containIP(rule.getNw_src_prefix(), + rule.getNw_src_maskbits(), ip)) { + if (checkIfRuleWorksInSwitch(rule.getId(), dpid)) { + continue; + } + String flowName = "ACLRule_" + rule.getId() + "_" + + dpid; + generateFlow(rule, dpid, flowName); } - String flowName = "ACLRule_" + rule.getId() + "_" + dpid; - ruleId2FlowName.get(rule.getId()).add(flowName); - ruleId2Dpid.get(rule.getId()).add(dpid); - generateFlow(rule, dpid, flowName); - } - } else { - if (IPAddressUtil.containIP(rule.getNw_dst_prefix(), - rule.getNw_dst_maskbits(), ip)) { - // check if there is a flow entry in the switch for the rule - if (ruleId2Dpid.get(rule.getId()).contains(dpid)) { - continue; + } else { + if (IPAddressUtil.containIP(rule.getNw_dst_prefix(), + rule.getNw_dst_maskbits(), ip)) { + if (checkIfRuleWorksInSwitch(rule.getId(), dpid)) { + continue; + } + String flowName = "ACLRule_" + rule.getId() + "_" + + dpid; + generateFlow(rule, dpid, flowName); } - String flowName = "ACLRule_" + rule.getId() + "_" + dpid; - ruleId2FlowName.get(rule.getId()).add(flowName); - ruleId2Dpid.get(rule.getId()).add(dpid); - generateFlow(rule, dpid, flowName); } } } @@ -389,24 +413,30 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { } + @Override + public void deviceIPV6AddrChanged(IDevice device) { + logger.debug("IPv6 not implemented in ACL. Device changed: {}", device.toString()); + } + @Override public void deviceIPV4AddrChanged(IDevice device) { - + SwitchPort[] switchPort = device.getAttachmentPoints(); IPv4Address[] ips = device.getIPv4Addresses(); - - String dpid = HexString.toHexString(switchPort[0].getSwitchDPID().getLong()); + + String dpid = HexString.toHexString(switchPort[0].getSwitchDPID() + .getLong()); String ip = null; - - // some device may first appear with no IP address(default set to 0.0.0.0), ignore it - for(IPv4Address i : ips){ - if(i.getInt() != 0){ + // some device may first appear with no IP address(default set to + // 0.0.0.0), ignore it + for (IPv4Address i : ips) { + if (i.getInt() != 0) { ip = IPv4.fromIPv4Address(i.getInt()); break; } } - - logger.info("New AP added. [dpid:" + dpid + " ip:" + ip + "]"); + + logger.debug("AP(dpid:{},ip:{}) is added", dpid, ip); AP ap = new AP(ip, dpid); apManager.addAP(ap); processAPAdded(ap); @@ -419,7 +449,7 @@ public class ACL implements IACLService, IFloodlightModule, IDeviceListener { @Override public String getName() { - return null; + return "ACL manager"; } @Override diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java b/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java deleted file mode 100644 index 3ba9e3da0c649c86405a324777aa1742759b8fd2..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/accesscontrollist/FlowPusher.java +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2015, Big Switch Networks, Inc. - * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China - * Advisers: Keqiu Li and Heng Qi - * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) - * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. You may obtain - * a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - **/ - -package net.floodlightcontroller.accesscontrollist; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import net.floodlightcontroller.core.IFloodlightProviderService; -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; -import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher; -import net.floodlightcontroller.storage.IStorageSourceService; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class FlowPusher implements IFloodlightModule { - - protected IFloodlightProviderService floodlightProvider; - protected static Logger logger; - protected IStorageSourceService storageSource; - protected IStaticFlowEntryPusherService sfp; - - @Override - public Collection<Class<? extends IFloodlightService>> getModuleServices() { - return null; - } - - @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { - return null; - } - - @Override - public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { - Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); - l.add(IFloodlightProviderService.class); - l.add(IStaticFlowEntryPusherService.class); - l.add(IStorageSourceService.class); - return l; - } - - @Override - public void init(FloodlightModuleContext context) - throws FloodlightModuleException { - floodlightProvider = context - .getServiceImpl(IFloodlightProviderService.class); - storageSource = context.getServiceImpl(IStorageSourceService.class); - sfp = context.getServiceImpl(IStaticFlowEntryPusherService.class); - logger = LoggerFactory.getLogger(FlowPusher.class); - } - - @Override - public void startUp(FloodlightModuleContext context) - throws FloodlightModuleException { - - HashMap<String,Object> flow = new HashMap<String,Object>(); - - flow.put(StaticFlowEntryPusher.COLUMN_TP_DST, "80"); - flow.put(StaticFlowEntryPusher.COLUMN_NW_DST, "10.0.0.2"); - flow.put(StaticFlowEntryPusher.COLUMN_NW_SRC, "10.0.0.1"); - flow.put(StaticFlowEntryPusher.COLUMN_PRIORITY, "30001"); - flow.put(StaticFlowEntryPusher.COLUMN_NAME, "flow1"); - flow.put(StaticFlowEntryPusher.COLUMN_ACTIVE, Boolean.toString(true)); - flow.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, "2048"); - flow.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, "6"); - flow.put(StaticFlowEntryPusher.COLUMN_SWITCH, "00:00:00:00:00:00:00:01"); - flow.put(StaticFlowEntryPusher.COLUMN_ACTIONS, "output=controller"); - - storageSource.insertRowAsync(StaticFlowEntryPusher.TABLE_NAME, flow); - -// flow.put("tp_dst", "80"); -// flow.put("nw_dst", "10.0.0.3"); -// flow.put("nw_src", "10.0.0.1"); -// flow.put("priority", "30001"); -// flow.put("name", "flow2"); -// flow.put("active", Boolean.toString(true)); -// flow.put("dl_type", "2048"); -// flow.put("nw_proto", "6"); -// flow.put("switch_id", "00:00:00:00:00:00:00:01"); -// flow.put("actions", "output=controller"); -// -// storageSource.insertRowAsync("controller_staticflowtableentry", flow); - -// storageSource.deleteRowAsync("controller_staticflowtableentry", "flow1"); - } - -} diff --git a/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java index 9ce23238e8ebefe34397cae2deaf9566e841fdbb..50e0c08044ecaa27a23b689359c7897b1c6388dc 100644 --- a/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java +++ b/src/main/java/net/floodlightcontroller/accesscontrollist/IACLService.java @@ -1,42 +1,56 @@ -/** - * Copyright 2015, Big Switch Networks, Inc. - * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China - * Advisers: Keqiu Li and Heng Qi - * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) - * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. You may obtain - * a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - **/ - -package net.floodlightcontroller.accesscontrollist; - -import java.util.List; - -import net.floodlightcontroller.core.module.IFloodlightService; - -public interface IACLService extends IFloodlightService { - - public List<ACLRule> getRules(); - - /** - * add a new ACL rule - * @param rule - * @return whether the rule is added successfully - */ - public boolean addRule(ACLRule rule); - - public void removeRule(int ruleid); - - public void removeAllRules(); - -} +/** + * Copyright 2015, Big Switch Networks, Inc. + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li and Heng Qi + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ + +package net.floodlightcontroller.accesscontrollist; + +import java.util.List; + +import net.floodlightcontroller.core.module.IFloodlightService; + +/** + * Service interface exported by ACL module + */ +public interface IACLService extends IFloodlightService { + + /** + * Gets a list containing all ACL rules. + * @return a list containing all ACL rules + */ + public List<ACLRule> getRules(); + + /** + * Add a new ACL rule. + * @param rule ACL rule + * @return true if successfully added, otherwise false + */ + public boolean addRule(ACLRule rule); + + /** + * Removes an existing ACL rule by rule id. + * @param ruleId ACL rule identifier + */ + public void removeRule(int ruleid); + + /** + * Clears ACL and resets all. + */ + public void removeAllRules(); + +} diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java index ccb68176f6086b187071cb4fc9fbf01e9e3df40d..df2c58a743918cc6c27aa89ec7e94dd064b65ed6 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitch.java @@ -116,7 +116,13 @@ public interface IOFSwitch extends IOFMessageWriter { Set<OFCapabilities> getCapabilities(); - short getTables(); + /** + * Get the specific TableIds according to the ofp_table_features. + * Not all switches have sequential TableIds, so this will give the + * specific TableIds used by the switch. + * @return + */ + Collection<TableId> getTables(); /** * @return a copy of the description statistics for this switch @@ -350,5 +356,11 @@ public interface IOFSwitch extends IOFMessageWriter { * @return The table features or null if no features are known for the table requested. */ public TableFeatures getTableFeatures(TableId table); + + /** + * Get the number of tables as returned by the ofp_features_reply. + * @return + */ + short getNumTables(); } diff --git a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java b/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java index a14ca20199c549213544d85c73a4b7ade72058ed..47edf3a08a1db86de881f516ea607134f9ebe34b 100644 --- a/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java +++ b/src/main/java/net/floodlightcontroller/core/IOFSwitchBackend.java @@ -27,8 +27,8 @@ import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; import org.projectfloodlight.openflow.protocol.OFPortStatus; -import org.projectfloodlight.openflow.protocol.OFTableFeatures; import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply; +import org.projectfloodlight.openflow.types.TableId; import net.floodlightcontroller.util.OrderedCollection; @@ -194,4 +194,35 @@ public interface IOFSwitchBackend extends IOFSwitch { * @return true if another viable master exists */ boolean hasAnotherMaster(); + + /** + * In OF1.3+ switches, the table miss behavior is defined by a flow. + * We assume the default behavior is to forward to the controller, but + * not all tables need that behavior if a limited set of tables are used. + * So, we can cap the number of tables we set this flow in to reduce + * clutter in API output and to reduce memory consumption on the switch. + * + * This gets the TableId cap set for this particular switch. + * + * @return, the highest TableId that should receive a table-miss flow + */ + TableId getMaxTableForTableMissFlow(); + + /** + * In OF1.3+ switches, the table miss behavior is defined by a flow. + * We assume the default behavior is to forward to the controller, but + * not all tables need that behavior if a limited set of tables are used. + * So, we can cap the number of tables we set this flow in to reduce + * clutter in API output and to reduce memory consumption on the switch. + * + * This sets the TableId cap set for this particular switch. If the max + * desired is higher than the number of tables this switch supports, the + * max table supported will be used: + * + * set_max_table = max_supported <= max ? max_supported-1 : max + * + * @param max, the highest TableId that should receive a table-miss flow + * @return the TableId set as the highest + */ + TableId setMaxTableForTableMissFlow(TableId max); } diff --git a/src/main/java/net/floodlightcontroller/core/OFConnection.java b/src/main/java/net/floodlightcontroller/core/OFConnection.java index 444e07d7338b1502e32e91f444ae4c1c82ab31f9..36efb215975f38dbe22b41433485c0ba2a49d9b1 100644 --- a/src/main/java/net/floodlightcontroller/core/OFConnection.java +++ b/src/main/java/net/floodlightcontroller/core/OFConnection.java @@ -411,6 +411,4 @@ public class OFConnection implements IOFConnection, IOFConnectionBackend{ } } - - } diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitch.java b/src/main/java/net/floodlightcontroller/core/OFSwitch.java index 8b401dad680cdda48eccaa36fad5db0b987d08a3..146bfe19179531501d1f36c66a0404575f70c7ce 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitch.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitch.java @@ -97,7 +97,8 @@ public class OFSwitch implements IOFSwitchBackend { protected Set<OFCapabilities> capabilities; protected long buffers; protected Set<OFActionType> actions; - protected short tables; + protected Collection<TableId> tables; + protected short nTables; protected final DatapathId datapathId; private Map<TableId, TableFeatures> tableFeaturesByTableId; @@ -112,8 +113,6 @@ public class OFSwitch implements IOFSwitchBackend { */ private final PortManager portManager; - //private final TableManager tableManager; - private volatile boolean connected; private volatile OFControllerRole role; @@ -125,6 +124,8 @@ public class OFSwitch implements IOFSwitchBackend { private SwitchStatus status; public static final int OFSWITCH_APP_ID = ident(5); + + private TableId maxTableToGetTableMissFlow = TableId.of(4); /* this should cover most HW switches that have a couple SW flow tables */ static { AppCookie.registerApp(OFSwitch.OFSWITCH_APP_ID, "switch"); @@ -164,6 +165,7 @@ public class OFSwitch implements IOFSwitchBackend { this.setAttribute(PROP_SUPPORTS_OFPP_TABLE, Boolean.TRUE); this.tableFeaturesByTableId = new HashMap<TableId, TableFeatures>(); + this.tables = new ArrayList<TableId>(); } private static int ident(int i) { @@ -715,6 +717,7 @@ public class OFSwitch implements IOFSwitchBackend { log.trace("Channel: {}, Connected: {}", connections.get(OFAuxId.MAIN).getRemoteInetAddress(), connections.get(OFAuxId.MAIN).isConnected()); if (isActive()) { connections.get(OFAuxId.MAIN).write(m); + switchManager.handleOutgoingMessage(this, m); } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } @@ -746,6 +749,7 @@ public class OFSwitch implements IOFSwitchBackend { public void write(OFMessage m, LogicalOFMessageCategory category) { if (isActive()) { this.getConnection(category).write(m); + switchManager.handleOutgoingMessage(this, m); } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } @@ -755,6 +759,10 @@ public class OFSwitch implements IOFSwitchBackend { public void write(Iterable<OFMessage> msglist, LogicalOFMessageCategory category) { if (isActive()) { this.getConnection(category).write(msglist); + + for(OFMessage m : msglist) { + switchManager.handleOutgoingMessage(this, m); + } } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } @@ -785,6 +793,10 @@ public class OFSwitch implements IOFSwitchBackend { public void write(Iterable<OFMessage> msglist) { if (isActive()) { connections.get(OFAuxId.MAIN).write(msglist); + + for(OFMessage m : msglist) { + switchManager.handleOutgoingMessage(this, m); + } } else { log.warn("Attempted to write to switch {} that is SLAVE.", this.getId().toString()); } @@ -816,10 +828,11 @@ public class OFSwitch implements IOFSwitchBackend { this.buffers = featuresReply.getNBuffers(); if (featuresReply.getVersion().compareTo(OFVersion.OF_13) < 0 ) { - // FIXME:LOJI: OF1.3 has per table actions. This needs to be modeled / handled here + /* OF1.3+ Per-table actions are set later in the OFTableFeaturesRequest/Reply */ this.actions = featuresReply.getActions(); } - this.tables = featuresReply.getNTables(); + + this.nTables = featuresReply.getNTables(); } @Override @@ -869,6 +882,7 @@ public class OFSwitch implements IOFSwitchBackend { List<OFTableFeatures> tfs = reply.getEntries(); for (OFTableFeatures tf : tfs) { tableFeaturesByTableId.put(tf.getTableId(), TableFeatures.of(tf)); + tables.add(tf.getTableId()); log.trace("Received TableFeatures for TableId {}, TableName {}", tf.getTableId().toString(), tf.getName()); } } @@ -1075,9 +1089,18 @@ public class OFSwitch implements IOFSwitchBackend { } + /** + * This performs a copy on each 'get'. + * Use sparingly for good performance. + */ + @Override + public Collection<TableId> getTables() { + return new ArrayList<TableId>(tables); + } + @Override - public short getTables() { - return tables; + public short getNumTables() { + return this.nTables; } @Override @@ -1208,4 +1231,19 @@ public class OFSwitch implements IOFSwitchBackend { public TableFeatures getTableFeatures(TableId table) { return tableFeaturesByTableId.get(table); } + + @Override + public TableId getMaxTableForTableMissFlow() { + return maxTableToGetTableMissFlow; + } + + @Override + public TableId setMaxTableForTableMissFlow(TableId max) { + if (max.getValue() >= nTables) { + maxTableToGetTableMissFlow = TableId.of(nTables - 1 < 0 ? 0 : nTables - 1); + } else { + maxTableToGetTableMissFlow = max; + } + return maxTableToGetTableMissFlow; + } } diff --git a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java index ea583510844a32655d7019afe2a10d620a02c892..c537ddd64ecd177c46cbbeca41bd88a6873a5b8b 100644 --- a/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java +++ b/src/main/java/net/floodlightcontroller/core/SwitchSyncRepresentation.java @@ -136,7 +136,7 @@ public class SwitchSyncRepresentation { public SwitchSyncRepresentation(IOFSwitch sw) { this.dpid = sw.getId(); this.buffers = sw.getBuffers(); - this.tables = sw.getTables(); + this.tables = (short) sw.getNumTables(); this.capabilities = sw.getCapabilities(); this.actions = sw.getActions(); this.ports = toSyncedPortList(sw.getPorts()); diff --git a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java index 787acc10949e997f287001ed5344114c73bc0000..e3b0d8ea910f35950a7aa8e4738d1f24d9e637c9 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java +++ b/src/main/java/net/floodlightcontroller/core/internal/IOFSwitchManager.java @@ -5,6 +5,7 @@ import java.util.List; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IOFConnectionBackend; import net.floodlightcontroller.core.IOFSwitch.SwitchStatus; +import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.IOFSwitchBackend; import net.floodlightcontroller.core.IOFSwitchDriver; import net.floodlightcontroller.core.LogicalOFMessageCategory; @@ -55,6 +56,13 @@ public interface IOFSwitchManager { * @param bContext the Floodlight context of the message, normally null in this case. */ void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext); + + /** + * Process written messages through the message listeners for the controller + * @param sw The switch being written to + * @param m the message + */ + public void handleOutgoingMessage(IOFSwitch sw, OFMessage m); /** * Gets an unmodifiable collection of OFSwitchHandshakeHandlers diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java index 6b165b5cab61b015996b90c3d9fd5af976e7bc38..a6702b74f6b014a498c41c0f9263e5cba8742eaa 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFChannelHandler.java @@ -2,7 +2,9 @@ package net.floodlightcontroller.core.internal; import java.io.IOException; import java.nio.channels.ClosedChannelException; +import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.concurrent.RejectedExecutionException; @@ -20,6 +22,7 @@ import org.jboss.netty.handler.timeout.IdleStateEvent; import org.jboss.netty.handler.timeout.IdleStateHandler; import org.jboss.netty.handler.timeout.ReadTimeoutException; import org.jboss.netty.util.Timer; + import net.floodlightcontroller.core.IOFConnectionBackend; import net.floodlightcontroller.core.OFConnection; import net.floodlightcontroller.core.annotations.LogMessageDoc; @@ -29,6 +32,7 @@ import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHa import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleReadTimeout; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineIdleWriteTimeout; import net.floodlightcontroller.debugcounter.IDebugCounterService; + import org.projectfloodlight.openflow.exceptions.OFParseError; import org.projectfloodlight.openflow.protocol.OFEchoReply; import org.projectfloodlight.openflow.protocol.OFEchoRequest; @@ -39,9 +43,16 @@ import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFeaturesReply; import org.projectfloodlight.openflow.protocol.OFFeaturesRequest; import org.projectfloodlight.openflow.protocol.OFHello; +import org.projectfloodlight.openflow.protocol.OFHelloElem; +import org.projectfloodlight.openflow.protocol.OFHelloElemVersionbitmap; import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.ver13.OFHelloElemTypeSerializerVer13; +import org.projectfloodlight.openflow.protocol.ver14.OFHelloElemTypeSerializerVer14; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.U32; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,10 +73,11 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { private Channel channel; private final Timer timer; private volatile OFChannelState state; - private OFFactory factory = OFFactories.getFactory(OFVersion.OF_14); + private OFFactory factory; private OFFeaturesReply featuresReply; private volatile OFConnection connection; private final IDebugCounterService debugCounters; + private final List<U32> ofBitmaps; /** transaction Ids to use during handshake. Since only one thread * calls into the OFChannelHandler we don't need atomic. @@ -112,6 +124,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { illegalMessageReceived(m); } + void processOFPortStatus(OFPortStatus m) { + unhandledMessageReceived(m); + } + private final boolean channelHandshakeComplete; OFChannelState(boolean handshakeComplete) { @@ -253,13 +269,16 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { case EXPERIMENTER: processOFExperimenter((OFExperimenter)m); break; - /* echos can be sent at any time */ + /* echos can be sent at any time */ case ECHO_REPLY: processOFEchoReply((OFEchoReply)m); break; case ECHO_REQUEST: processOFEchoRequest((OFEchoRequest)m); break; + case PORT_STATUS: + processOFPortStatus((OFPortStatus)m); + break; default: illegalMessageReceived(m); break; @@ -305,14 +324,49 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { @Override void processOFHello(OFHello m) throws IOException { - OFVersion version = m.getVersion(); - /* Choose the lower of the two supported versions. */ - if (version.compareTo(factory.getVersion()) < 0) { - factory = OFFactories.getFactory(version); + OFVersion theirVersion = m.getVersion(); + OFVersion commonVersion = null; + /* First, check if there's a version bitmap supplied. WE WILL ALWAYS HAVE a controller-provided version bitmap. */ + if (theirVersion.compareTo(OFVersion.OF_13) >= 0 && !m.getElements().isEmpty()) { + List<U32> bitmaps = new ArrayList<U32>(); + List<OFHelloElem> elements = m.getElements(); + /* Grab all bitmaps supplied */ + for (OFHelloElem e : elements) { + if (m.getVersion().equals(OFVersion.OF_13) + && e.getType() == OFHelloElemTypeSerializerVer13.VERSIONBITMAP_VAL) { + bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps()); + } else if (m.getVersion().equals(OFVersion.OF_14) + && e.getType() == OFHelloElemTypeSerializerVer14.VERSIONBITMAP_VAL) { + bitmaps.addAll(((OFHelloElemVersionbitmap) e).getBitmaps()); + } + } + /* Lookup highest, common supported OpenFlow version */ + commonVersion = computeOFVersionFromBitmap(bitmaps); + if (commonVersion == null) { + log.error("Could not negotiate common OpenFlow version for {} with greatest version bitmap algorithm.", channel.getRemoteAddress()); + channel.disconnect(); + return; + } else { + log.info("Negotiated OpenFlow version of {} for {} with greatest version bitmap algorithm.", commonVersion.toString(), channel.getRemoteAddress()); + factory = OFFactories.getFactory(commonVersion); + OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class); + decoder.setVersion(commonVersion); + } + } + /* If there's not a bitmap present, choose the lower of the two supported versions. */ + else if (theirVersion.compareTo(factory.getVersion()) < 0) { + log.info("Negotiated down to switch OpenFlow version of {} for {} using lesser hello header algorithm.", theirVersion.toString(), channel.getRemoteAddress()); + factory = OFFactories.getFactory(theirVersion); + OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class); + decoder.setVersion(theirVersion); } /* else The controller's version is < or = the switch's, so keep original controller factory. */ - - OFMessageDecoder decoder = pipeline.get(OFMessageDecoder.class); - decoder.setVersion(version); + else if (theirVersion.equals(factory.getVersion())) { + log.info("Negotiated equal OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress()); + } + else { + log.info("Negotiated down to controller OpenFlow version of {} for {} using lesser hello header algorithm.", factory.getVersion().toString(), channel.getRemoteAddress()); + } + setState(new WaitFeaturesReplyState()); } @@ -341,10 +395,50 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { setState(new CompleteState()); } + + @Override + void processOFHello(OFHello m) throws IOException { + /* + * Brocade switches send a second hello after + * the controller responds with its hello. This + * might be to confirm the protocol version used, + * but isn't defined in the OF specification. + * + * We will ignore such hello messages assuming + * the version of the hello is correct according + * to the algorithm in the spec. + * + * TODO Brocade also sets the XID of this second + * hello as the same XID the controller used. + * Checking for this might help to assure we're + * really dealing with the situation we think + * we are. + */ + if (m.getVersion().equals(factory.getVersion())) { + log.warn("Ignoring second hello from {} in state {}. Might be a Brocade.", channel.getRemoteAddress(), state.toString()); + } else { + super.processOFHello(m); /* Versions don't match as they should; abort */ + } + } + + @Override + void processOFPortStatus(OFPortStatus m) { + log.warn("Ignoring PORT_STATUS message from {} during OpenFlow channel establishment. Ports will be explicitly queried in a later state.", channel.getRemoteAddress()); + } + @Override void enterState() throws IOException { sendFeaturesRequest(); } + + @Override + void processOFMessage(OFMessage m) throws IOException { + if (m.getType().equals(OFType.PACKET_IN)) { + log.warn("Ignoring PACKET_IN message from {} during OpenFlow channel establishment.", channel.getRemoteAddress()); + } else { + super.processOFMessage(m); + } + } }; /** @@ -402,7 +496,9 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { @Nonnull INewOFConnectionListener newConnectionListener, @Nonnull ChannelPipeline pipeline, @Nonnull IDebugCounterService debugCounters, - @Nonnull Timer timer) { + @Nonnull Timer timer, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory) { Preconditions.checkNotNull(switchManager, "switchManager"); Preconditions.checkNotNull(newConnectionListener, "connectionOpenedListener"); @@ -416,10 +512,45 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { this.counters = switchManager.getCounters(); this.state = new InitState(); this.timer = timer; + this.ofBitmaps = ofBitmaps; + this.factory = defaultFactory; log.debug("constructor on OFChannelHandler {}", String.format("%08x", System.identityHashCode(this))); } + /** + * Determine the highest supported version of OpenFlow in common + * between both our OFVersion bitmap and the switch's. + * + * @param theirs, the version bitmaps of the switch + * @return the highest OFVersion in common b/t the two + */ + private OFVersion computeOFVersionFromBitmap(List<U32> theirs) { + Iterator<U32> theirsItr = theirs.iterator(); + Iterator<U32> oursItr = ofBitmaps.iterator(); + OFVersion version = null; + int pos = 0; + int size = 32; + while (theirsItr.hasNext() && oursItr.hasNext()) { + int t = theirsItr.next().getRaw(); + int o = oursItr.next().getRaw(); + + int common = t & o; /* Narrow down the results to the common bits */ + for (int i = 0; i < size; i++) { /* Iterate over and locate the 1's */ + int tmp = common & (1 << i); /* Select the bit of interest, 0-31 */ + if (tmp != 0) { /* Is the version of this bit in common? */ + for (OFVersion v : OFVersion.values()) { /* Which version does this bit represent? */ + if (v.getWireVersion() == i + (size * pos)) { + version = v; + } + } + } + } + pos++; /* OFVersion position. 1-31 = 1, 32 - 63 = 2, etc. Inc at end so it starts at 0. */ + } + return version; + } + /** * Determines if the entire switch handshake is complete (channel+switch). * If the channel handshake is complete the call is forwarded to the @@ -597,7 +728,7 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { log.warn("Could not process message: queue full"); counters.rejectedExecutionException.increment(); } else if (e.getCause() instanceof IllegalArgumentException) { - log.error("Could not decode OpenFlow protocol version from switch {}. Perhaps the switch is trying to use SSL and the controller is not (or vice versa)? {}", getConnectionInfoString(), e.getCause()); + log.error("Illegal argument exception with switch {}. {}", getConnectionInfoString(), e.getCause()); counters.switchSslConfigurationError.increment(); ctx.getChannel().close(); } else { @@ -733,11 +864,21 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { */ private void sendHelloMessage() throws IOException { // Send initial hello message - // FIXME:LOJI: Haven't negotiated version yet, assume 1.3 - OFHello.Builder builder = factory.buildHello() - .setXid(handshakeTransactionIds--); - // FIXME: Need to add code here to set the version bitmap hello element - OFHello m = builder.build(); + + OFHello.Builder builder = factory.buildHello(); + + /* Our highest-configured OFVersion does support version bitmaps, so include it */ + if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { + List<OFHelloElem> he = new ArrayList<OFHelloElem>(); + he.add(factory.buildHelloElemVersionbitmap() + .setBitmaps(ofBitmaps) + .build()); + builder.setElements(he); + } + + OFHello m = builder.setXid(handshakeTransactionIds--) + .build(); + channel.write(Collections.singletonList(m)); log.debug("Send hello: {}", m); } diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java index a17e7d5ae859978d5d7832ae10eed3273d7dc7b2..0e6c15daebc9bb3ba5266359eeae403f01f0af6c 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchHandshakeHandler.java @@ -3,6 +3,7 @@ package net.floodlightcontroller.core.internal; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -24,10 +25,13 @@ import net.floodlightcontroller.core.SwitchDescription; import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.annotations.LogMessageDocs; import net.floodlightcontroller.core.internal.OFSwitchAppHandshakePlugin.PluginResultType; +import net.floodlightcontroller.util.OFDPAUtils; +import org.projectfloodlight.openflow.protocol.OFActionType; import org.projectfloodlight.openflow.protocol.OFBadRequestCode; import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFBarrierRequest; +import org.projectfloodlight.openflow.protocol.OFBucket; import org.projectfloodlight.openflow.protocol.OFControllerRole; import org.projectfloodlight.openflow.protocol.OFDescStatsReply; import org.projectfloodlight.openflow.protocol.OFDescStatsRequest; @@ -44,6 +48,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode; import org.projectfloodlight.openflow.protocol.OFFlowRemoved; import org.projectfloodlight.openflow.protocol.OFGetConfigReply; import org.projectfloodlight.openflow.protocol.OFGetConfigRequest; +import org.projectfloodlight.openflow.protocol.OFGroupAdd; import org.projectfloodlight.openflow.protocol.OFGroupDelete; import org.projectfloodlight.openflow.protocol.OFGroupType; import org.projectfloodlight.openflow.protocol.OFMessage; @@ -51,6 +56,7 @@ import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole; import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply; import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest; import org.projectfloodlight.openflow.protocol.OFPacketIn; +import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply; import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply; @@ -66,14 +72,22 @@ import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsRequest; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.actionid.OFActionId; +import org.projectfloodlight.openflow.protocol.actionid.OFActionIdOutput; import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg; +import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; +import org.projectfloodlight.openflow.protocol.match.MatchField; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFGroup; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U16; import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.VlanVid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -103,7 +117,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { private OFFactory factory = OFFactories.getFactory(OFVersion.OF_14); private final OFFeaturesReply featuresReply; private final Timer timer; - + private volatile OFControllerRole initialRole = null; private final ArrayList<OFPortStatus> pendingPortStatusMsg; @@ -471,15 +485,25 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { .setGroupType(OFGroupType.SELECT) .build(); this.sw.write(delgroup); + + /* + * Make sure we allow these operations to complete before proceeding. + */ + OFBarrierRequest barrier = factory.buildBarrierRequest() + .setXid(handshakeTransactionIds--) + .build(); + sw.write(barrier); } } /** - * Adds an initial table-miss flow to each - * and every table on the switch. This replaces the default behavior of - * forwarding table-miss packets to the controller. The table-miss flows - * inserted will forward all packets that do not match a flow to the - * controller for processing. + * Adds an initial table-miss flow to tables on the switch. + * This replaces the default behavior of forwarding table-miss packets + * to the controller. The table-miss flows inserted will forward all + * packets that do not match a flow to the controller for processing. + * + * The OFSwitchManager is checked for used-defined behavior and default + * max table to try to use. * * Adding the default flow only applies to OpenFlow 1.3+ switches, which * remove the default forward-to-controller behavior of flow tables. @@ -498,22 +522,185 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { .setOutPort(OFPort.CONTROLLER) .build(); this.sw.write(deleteFlow); - + ArrayList<OFAction> actions = new ArrayList<OFAction>(1); actions.add(factory.actions().output(OFPort.CONTROLLER, 0xffFFffFF)); ArrayList<OFMessage> flows = new ArrayList<OFMessage>(); - for (int tableId = 0; tableId < this.sw.getTables(); tableId++) { - OFFlowAdd defaultFlow = this.factory.buildFlowAdd() - .setTableId(TableId.of(tableId)) - .setPriority(0) - .setActions(actions) - .build(); - flows.add(defaultFlow); + + /* If we received a table features reply, iterate over the tables */ + if (!this.sw.getTables().isEmpty()) { + short missCount = 0; + for (TableId tid : this.sw.getTables()) { + /* Only add the flow if the table exists and if it supports sending to the controller */ + TableFeatures tf = this.sw.getTableFeatures(tid); + if (tf != null && (missCount < this.sw.getMaxTableForTableMissFlow().getValue())) { + for (OFActionId aid : tf.getPropApplyActionsMiss().getActionIds()) { + if (aid.getType() == OFActionType.OUTPUT) { /* The assumption here is that OUTPUT includes the special port CONTROLLER... */ + OFFlowAdd defaultFlow = this.factory.buildFlowAdd() + .setTableId(tid) + .setPriority(0) + .setActions(actions) + .build(); + flows.add(defaultFlow); + break; /* Stop searching for actions and go to the next table in the list */ + } + } + } + missCount++; + } + } else { /* Otherwise, use the number of tables starting at TableId=0 as indicated in the features reply */ + short missCount = 0; + for (short tid = 0; tid < this.sw.getNumTables(); tid++, missCount++) { + if (missCount < this.sw.getMaxTableForTableMissFlow().getValue()) { /* Only insert if we want it */ + OFFlowAdd defaultFlow = this.factory.buildFlowAdd() + .setTableId(TableId.of(tid)) + .setPriority(0) + .setActions(actions) + .build(); + flows.add(defaultFlow); + } + } } this.sw.write(flows); } } + private void addBroadcomOFDPAFlows() { + /* + * By default, we'll assume everyone's on the same VLAN, + * and all switch ports are configured as access ports. + * As such, all packets on the wire will be untagged + * and will only be tagged internally in the switch for + * pipeline processing. + * + * If you would like to configure trunks on switches, then + * each switch will need to be configured specifically, as + * we won't be able to automatically handle such a topology. + * + * Ingress port table (0) = empty --> default to VLAN table + * VLAN table (10) = match untagged, apply internal tag, goto termination MAC + * Termination MAC table (20) = match vlan tag and dst MAC, goto bridging table (default miss-->bridging) + * match only vlan tag, goto controller (DLF or Dest Lookup Failure) + * Bridging table (50) = default send to policy ACL table + * Policy ACL table (60) = priority=0 go to controller + * write action group of forwarding decision + * Group tables + * One per interface per VLAN + * One per VLAN (for flooding) + * + * TABLE_INGRESS = 0 + * TABLE_VLAN = 10 + * TABLE_MAC = 20 + * TABLE_UNICAST = 30 + * TABLE_MULTICAST = 40 + * TABLE_BRIDGING = 50 + * TABLE_ACL = 60 + */ + + /* + * Add flow to match all untagged packets from all ports in VLAN table + */ + List<OFAction> al = new ArrayList<OFAction>(1); + /* al.add(factory.actions().pushVlan(EthType.IPv4)); might not need this */ + al.add(factory.actions().setVlanVid(VlanVid.ofVlan(1))); /* we'll use 1 internally, just because */ + + List<OFInstruction> il = new ArrayList<OFInstruction>(2); + il.add(factory.instructions().gotoTable(TableId.of(20))); /* 20 is the termination MAC table */ + il.add(factory.instructions().applyActions(al)); + OFFlowAdd fa = factory.buildFlowAdd() + .setTableId(TableId.of(10)) + .setOutPort(OFPort.ANY) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.ZERO) + .setMatch(factory.buildMatch() + .setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED) /* this flow handles untagged */ + /* do we have to match on the in port here? */ + .build() + ) + .setInstructions(il) + .setPriority(1000) + .build(); + sw.write(fa); + + /* + * The termination MAC flow table must proactively forward to controller specific dst MACs, + * so we need to wait to do wildcarded dst MACs in bridging table upon a miss. Send to bridging + * table by default here. + */ + + /* + * Add flow to match all vlan=1 packets to forward to controller in bridging table (DLF). + * Default is to send to policy ACL if this does not match. + */ + al = new ArrayList<OFAction>(1); + al.add(factory.actions().output(OFPort.CONTROLLER, 0xffFFffFF)); + + il = new ArrayList<OFInstruction>(1); + il.add(factory.instructions().applyActions(al)); + fa = factory.buildFlowAdd() + .setTableId(TableId.of(50)) + .setOutPort(OFPort.ANY) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.ZERO) + .setMatch(factory.buildMatch() + .setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlan(1)) /* this flow handles recently-tagged VLAN=1 */ + .build() + ) + .setInstructions(il) + .setPriority(1) + .build(); + sw.write(fa); + + /* + * Lastly, add a group for flooding and for each port. + * This group is only for VLAN=1. + * + * The flood group has buckets with goto group actions + * for each port's individual L2 group for VLAN=1. + * + * This means we must first add the individual groups. + */ + ArrayList<OFBucket> buckets = new ArrayList<OFBucket>(); + for (OFPortDesc pd : this.sw.getPorts()) { + OFPort p = pd.getPortNo(); + if ((p.getShortPortNumber() & 0xFF00) == 0) { /* TODO Is this correct for special ports? */ + OFGroupAdd ga = factory.buildGroupAdd() + .setGroupType(OFGroupType.INDIRECT) + .setBuckets(Collections.singletonList(factory.buildBucket() + .setActions( + Collections.singletonList((OFAction) factory.actions().buildOutput() + .setMaxLen(0xffFFffFF) + .setPort(p) + .build())) + .build())) + .setGroup(OFDPAUtils.GroupIds.createL2Interface(p, VlanVid.ofVlan(100))) + .build(); + sw.write(ga); + + /* + * Add the port+bucket for creating the FLOOD group below. + * All L2_INTERFACE groups in a VLAN should be within a + * corresponding L2_FLOOD group of type ALL. + */ + buckets.add(factory.buildBucket().setActions( + Collections.singletonList( + (OFAction) factory.actions().buildOutput() + .setMaxLen(0xffFFffFF) + .setPort(p) + .build() + ) + ).build()); + } + } + + OFGroupAdd ga = factory.buildGroupAdd() + .setGroupType(OFGroupType.ALL) + .setBuckets(buckets) + .setGroup(OFDPAUtils.GroupIds.createL2Flood(U16.ZERO, VlanVid.ofVlan(100))) + .build(); + sw.write(ga); + } + /** * Default implementation for message handlers in any state. * @@ -766,7 +953,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { switchManager.notifyPortChanged(sw, ev.port, ev.type); } } - + /** * Handle a table features message. * @@ -858,7 +1045,6 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { } break; default: - illegalMessageReceived(m); break; } } @@ -1014,7 +1200,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { OFDescStatsReply descStatsReply = (OFDescStatsReply) m; SwitchDescription description = new SwitchDescription(descStatsReply); sw = switchManager.getOFSwitchInstance(mainConnection, description, factory, featuresReply.getDatapathId()); - switchManager.switchAdded(sw); + // set switch information // set features reply and channel first so we a DPID and // channel info. @@ -1022,6 +1208,11 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { if (portDescStats != null) { sw.setPortDescStats(portDescStats); } + /* + * Need to add after setting the features. + */ + switchManager.switchAdded(sw); + // Handle pending messages now that we have a sw object handlePendingPortStatusMessages(description); @@ -1042,7 +1233,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { sendHandshakeDescriptionStatsRequest(); } } - + /* * New state: WaitSwitchTableFeaturesReplyState */ @@ -1053,7 +1244,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { super(false); replies = new ArrayList<OFTableFeaturesStatsReply>(); } - + @Override /** * Accumulate a list of the OFTableFeaturesStatsReply's until there @@ -1076,23 +1267,23 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { /* should only receive TABLE_FEATURES here */ log.error("Received {} message but expected TABLE_FEATURES.", m.getStatsType().toString()); } - + } - + @Override void processOFError(OFErrorMsg m) { if ((m.getErrType() == OFErrorType.BAD_REQUEST) && ((((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.MULTIPART_BUFFER_OVERFLOW) - || ((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.BAD_STAT)) { + || ((OFBadRequestErrorMsg)m).getCode() == OFBadRequestCode.BAD_STAT)) { log.warn("Switch {} is {} but does not support OFTableFeaturesStats. Assuming all tables can perform any match, action, and instruction in the spec.", sw.getId().toString(), sw.getOFFactory().getVersion().toString()); } else { log.error("Received unexpected OFErrorMsg {} on switch {}.", m.toString(), sw.getId().toString()); } nextState(); - + } - + private void nextState() { /* move on to the next state */ sw.startDriverHandshake(); @@ -1102,7 +1293,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { setState(new WaitSwitchDriverSubHandshakeState()); } } - + @Override void enterState() { if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) < 0) { @@ -1111,7 +1302,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { sendHandshakeTableFeaturesRequest(); } } - + } public class WaitSwitchDriverSubHandshakeState extends OFSwitchHandshakeState { @@ -1319,18 +1510,37 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { super(true); } + private long sendBarrier() { + long xid = handshakeTransactionIds--; + OFBarrierRequest barrier = factory.buildBarrierRequest() + .setXid(xid) + .build(); + sw.write(barrier); /* don't use ListenableFuture here; we receive via barrier reply OR error (barrier unsupported) */ + return xid; + } + @Override void enterState() { - setSwitchStatus(SwitchStatus.MASTER); if (OFSwitchManager.clearTablesOnEachTransitionToMaster) { - log.info("Clearing flow tables of {} on recent transition to MASTER.", sw.getId().toString()); + log.info("Clearing flow tables of {} on upcoming transition to MASTER.", sw.getId().toString()); clearAllTables(); } else if (OFSwitchManager.clearTablesOnInitialConnectAsMaster && initialRole == null) { /* don't do it if we were slave first */ initialRole = OFControllerRole.ROLE_MASTER; - log.info("Clearing flow tables of {} on initial role as MASTER.", sw.getId().toString()); + log.info("Clearing flow tables of {} on upcoming initial role as MASTER.", sw.getId().toString()); clearAllTables(); } + + sendBarrier(); /* Need to make sure the tables are clear before adding default flows */ addDefaultFlows(); + + /* + * We also need a barrier between adding flows and notifying modules of the + * transition to master. Some modules might modify the flow tables and expect + * the clear/default flow operations above to have completed. + */ + sendBarrier(); + + setSwitchStatus(SwitchStatus.MASTER); } @LogMessageDoc(level="WARN", @@ -1439,7 +1649,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { void processOFFlowRemoved(OFFlowRemoved m) { dispatchMessage(m); } - + @Override void processOFStatsReply(OFStatsReply m) { // TODO Auto-generated method stub @@ -1762,7 +1972,7 @@ public class OFSwitchHandshakeHandler implements IOFConnectionListener { .build(); mainConnection.write(descStatsRequest); } - + /** * send a table features request */ diff --git a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java index 3c5ea1085267f0e30bec55659d0bdd049e5052ec..bace34438cd4a7c8080e3d36dcbc5dbe48c3fc58 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OFSwitchManager.java @@ -1,9 +1,13 @@ package net.floodlightcontroller.core.internal; +import java.io.IOException; import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -51,12 +55,16 @@ import net.floodlightcontroller.debugevent.IEventCategory; import net.floodlightcontroller.debugevent.MockDebugEventService; import org.projectfloodlight.openflow.protocol.OFControllerRole; +import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFeaturesReply; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; +import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U32; import org.sdnplatform.sync.IStoreClient; import org.sdnplatform.sync.IStoreListener; import org.sdnplatform.sync.ISyncService; @@ -65,6 +73,10 @@ import org.sdnplatform.sync.error.SyncException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.MappingJsonFactory; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -93,14 +105,20 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen private static String keyStorePassword; private static String keyStore; private static boolean useSsl = false; - + protected static boolean clearTablesOnInitialConnectAsMaster = false; protected static boolean clearTablesOnEachTransitionToMaster = false; + protected static Map<DatapathId, TableId> forwardToControllerFlowsUpToTableByDpid; + protected static TableId forwardToControllerFlowsUpToTable = TableId.of(4); /* this should cover most HW switches that have a couple SW-based flow tables */ + + protected static List<U32> ofBitmaps; + protected static OFFactory defaultFactory; + private ConcurrentHashMap<DatapathId, OFSwitchHandshakeHandler> switchHandlers; private ConcurrentHashMap<DatapathId, IOFSwitchBackend> switches; private ConcurrentHashMap<DatapathId, IOFSwitch> syncedSwitches; - + private Set<DatapathId> pastSwitches; private ISwitchDriverRegistry driverRegistry; @@ -166,7 +184,17 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen addUpdateToQueue(new SwitchUpdate(dpid, SwitchUpdateType.REMOVED)); oldSw.disconnect(); } - + + /* + * Set some other config options for this switch. + */ + if (sw.getOFFactory().getVersion().compareTo(OFVersion.OF_13) >= 0) { + if (forwardToControllerFlowsUpToTableByDpid.containsKey(sw.getId())) { + sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTableByDpid.get(sw.getId())); + } else { + sw.setMaxTableForTableMissFlow(forwardToControllerFlowsUpToTable); + } + } } @LogMessageDocs({ @@ -476,6 +504,7 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen public IOFSwitchBackend getOFSwitchInstance(IOFConnectionBackend connection, SwitchDescription description, OFFactory factory, DatapathId datapathId) { + return this.driverRegistry.getOFSwitchInstance(connection, description, factory, datapathId); } @@ -483,6 +512,11 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen public void handleMessage(IOFSwitchBackend sw, OFMessage m, FloodlightContext bContext) { floodlightProvider.handleMessage(sw, m, bContext); } + + @Override + public void handleOutgoingMessage(IOFSwitch sw, OFMessage m) { + floodlightProvider.handleOutgoingMessage(sw, m); + } @Override public void addOFSwitchDriver(String manufacturerDescriptionPrefix, @@ -643,6 +677,8 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen driverRegistry = new NaiveSwitchDriverRegistry(this); this.switchListeners = new CopyOnWriteArraySet<IOFSwitchListener>(); + + this.pastSwitches = new HashSet<DatapathId>(); /* TODO @Ryan try { @@ -679,13 +715,13 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen OFSwitchManager.keyStore = null; OFSwitchManager.keyStorePassword = null; } else { - log.warn("SSL enabled. Using secure connections between Floodlight and switches."); + log.info("SSL enabled. Using secure connections between Floodlight and switches."); log.info("SSL keystore path: {}, password: {}", path, (pass == null ? "" : pass)); OFSwitchManager.useSsl = true; OFSwitchManager.keyStore = path; OFSwitchManager.keyStorePassword = (pass == null ? "" : pass); } - + /* * Get config to define what to do when a switch connects. * @@ -693,33 +729,250 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen */ String clearInitial = configParams.get("clearTablesOnInitialHandshakeAsMaster"); String clearLater = configParams.get("clearTablesOnEachTransitionToMaster"); - + if (clearInitial == null || clearInitial.isEmpty() || (!clearInitial.equalsIgnoreCase("yes") && !clearInitial.equalsIgnoreCase("true") && - !clearInitial.equalsIgnoreCase("yep") && !clearInitial.equalsIgnoreCase("ja") && - !clearInitial.equalsIgnoreCase("stimmt"))) { - log.warn("Clear switch flow tables on initial handshake as master: FALSE"); + !clearInitial.equalsIgnoreCase("yep") && !clearInitial.equalsIgnoreCase("ja") && + !clearInitial.equalsIgnoreCase("stimmt"))) { + log.info("Clear switch flow tables on initial handshake as master: FALSE"); OFSwitchManager.clearTablesOnInitialConnectAsMaster = false; } else { - log.warn("Clear switch flow tables on initial handshake as master: TRUE"); + log.info("Clear switch flow tables on initial handshake as master: TRUE"); OFSwitchManager.clearTablesOnInitialConnectAsMaster = true; } - + if (clearLater == null || clearLater.isEmpty() || (!clearLater.equalsIgnoreCase("yes") && !clearLater.equalsIgnoreCase("true") && - !clearLater.equalsIgnoreCase("yep") && !clearLater.equalsIgnoreCase("ja") && - !clearLater.equalsIgnoreCase("stimmt"))) { - log.warn("Clear switch flow tables on each transition to master: FALSE"); + !clearLater.equalsIgnoreCase("yep") && !clearLater.equalsIgnoreCase("ja") && + !clearLater.equalsIgnoreCase("stimmt"))) { + log.info("Clear switch flow tables on each transition to master: FALSE"); OFSwitchManager.clearTablesOnEachTransitionToMaster = false; } else { - log.warn("Clear switch flow tables on each transition to master: TRUE"); + log.info("Clear switch flow tables on each transition to master: TRUE"); OFSwitchManager.clearTablesOnEachTransitionToMaster = true; } + + /* + * Get default max table for forward to controller flows. + * Internal default set as class variable at top of OFSwitchManager. + */ + String defaultFlowsUpToTable = configParams.get("defaultMaxTablesToReceiveTableMissFlow"); + /* Backward compatibility */ + if (defaultFlowsUpToTable == null || defaultFlowsUpToTable.isEmpty()) { + defaultFlowsUpToTable = configParams.get("defaultMaxTableToReceiveTableMissFlow"); + } + if (defaultFlowsUpToTable != null && !defaultFlowsUpToTable.isEmpty()) { + defaultFlowsUpToTable = defaultFlowsUpToTable.toLowerCase().trim(); + try { + forwardToControllerFlowsUpToTable = TableId.of(defaultFlowsUpToTable.startsWith("0x") + ? Integer.parseInt(defaultFlowsUpToTable.replaceFirst("0x", ""), 16) + : Integer.parseInt(defaultFlowsUpToTable)); + log.info("Setting {} as the default max tables to receive table-miss flow", forwardToControllerFlowsUpToTable.toString()); + } catch (IllegalArgumentException e) { + log.error("Invalid table ID {} for default max tables to receive table-miss flow. Using pre-set of {}", + defaultFlowsUpToTable, forwardToControllerFlowsUpToTable.toString()); + } + } else { + log.info("Default max tables to receive table-miss flow not configured. Using {}", forwardToControllerFlowsUpToTable.toString()); + } + + /* + * Get config to define which tables per switch will get a + * default forward-to-controller flow. This can be used to + * reduce the number of such flows if only a reduced set of + * tables are being used. + */ + String maxPerDpid = configParams.get("maxTablesToReceiveTableMissFlowPerDpid"); + /* Backward compatibility */ + if (maxPerDpid == null || maxPerDpid.isEmpty()) { + maxPerDpid = configParams.get("maxTableToReceiveTableMissFlowPerDpid"); + } + forwardToControllerFlowsUpToTableByDpid = jsonToSwitchTableIdMap(maxPerDpid); + + /* + * Get config to determine what versions of OpenFlow we will + * support. The versions will determine the hello's header + * version as well as the OF1.3.1 version bitmap contents. + */ + String protocols = configParams.get("supportedOpenFlowVersions"); + Set<OFVersion> ofVersions = new HashSet<OFVersion>(); + if (protocols != null && !protocols.isEmpty()) { + protocols = protocols.toLowerCase(); + /* + * Brute-force check for all known versions. + */ + if (protocols.contains("1.0") || protocols.contains("10")) { + ofVersions.add(OFVersion.OF_10); + } + if (protocols.contains("1.1") || protocols.contains("11")) { + ofVersions.add(OFVersion.OF_11); + } + if (protocols.contains("1.2") || protocols.contains("12")) { + ofVersions.add(OFVersion.OF_12); + } + if (protocols.contains("1.3") || protocols.contains("13")) { + ofVersions.add(OFVersion.OF_13); + } + if (protocols.contains("1.4") || protocols.contains("14")) { + ofVersions.add(OFVersion.OF_14); + } + /* + * TODO This will need to be updated if/when + * Loxi is updated to support > 1.4. + * + * if (protocols.contains("1.5") || protocols.contains("15")) { + * ofVersions.add(OFVersion.OF_15); + * } + */ + } else { + log.warn("Supported OpenFlow versions not specified. Using Loxi-defined {}", OFVersion.values()); + ofVersions.addAll(Arrays.asList(OFVersion.values())); + } + /* Sanity check */ + if (ofVersions.isEmpty()) { + throw new IllegalStateException("OpenFlow version list should never be empty at this point. Make sure it's being populated in OFSwitchManager's init function."); + } + defaultFactory = computeInitialFactory(ofVersions); + ofBitmaps = computeOurVersionBitmaps(ofVersions); + } + + /** + * Find the max version supplied in the supported + * versions list and use it as the default, which + * will subsequently be used in our hello message + * header's version field. + * + * The factory can be later "downgraded" to a lower + * version depending on what's computed during the + * version-negotiation part of the handshake. + * + * Assumption: The Set of OFVersion ofVersions + * variable has been set already and is NOT EMPTY. + * + * @return the highest-version OFFactory we support + */ + private OFFactory computeInitialFactory(Set<OFVersion> ofVersions) { + /* This should NEVER happen. Double-checking. */ + if (ofVersions == null || ofVersions.isEmpty()) { + throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager."); + } + OFVersion highest = null; + for (OFVersion v : ofVersions) { + if (highest == null) { + highest = v; + } else if (v.compareTo(highest) > 0) { + highest = v; + } + } + /* + * This assumes highest != null, which + * it won't be if the list of versions + * is not empty. + */ + return OFFactories.getFactory(highest); + } + + /** + * Based on the list of OFVersions provided as input (or from Loxi), + * create a list of bitmaps for use in version negotiation during a + * cross-version OpenFlow handshake where both parties support + * OpenFlow versions >= 1.3.1. + * + * Type Set is used as input to guarantee all unique versions. + * + * @param ofVersions, the list of bitmaps. Supply to an OFHello message. + * @return list of bitmaps for the versions of OpenFlow we support + */ + private List<U32> computeOurVersionBitmaps(Set<OFVersion> ofVersions) { + /* This should NEVER happen. Double-checking. */ + if (ofVersions == null || ofVersions.isEmpty()) { + throw new IllegalStateException("OpenFlow version list should never be null or empty at this point. Make sure it's set in the OFSwitchManager."); + } - String tablesToGetFTCFlow = configParams.get("addDefaultSendToControllerFlowInTables"); - if (tablesToGetFTCFlow == null || tablesToGetFTCFlow.isEmpty()) { - + int pos = 1; /* initial bitmap in list */ + int size = 32; /* size of a U32 */ + int tempBitmap = 0; /* maintain the current bitmap we're working on */ + List<U32> bitmaps = new ArrayList<U32>(); + ArrayList<OFVersion> sortedVersions = new ArrayList<OFVersion>(ofVersions); + Collections.sort(sortedVersions); + for (OFVersion v : sortedVersions) { + /* Move on to another bitmap */ + if (v.getWireVersion() > pos * size - 1 ) { + bitmaps.add(U32.ofRaw(tempBitmap)); + tempBitmap = 0; + pos++; + } + tempBitmap = tempBitmap | (1 << (v.getWireVersion() % size)); + } + if (tempBitmap != 0) { + bitmaps.add(U32.ofRaw(tempBitmap)); } + log.info("Computed OpenFlow version bitmap as {}", Arrays.asList(tempBitmap)); + return bitmaps; + } + + private static Map<DatapathId, TableId> jsonToSwitchTableIdMap(String json) { + MappingJsonFactory f = new MappingJsonFactory(); + JsonParser jp; + Map<DatapathId, TableId> retValue = new HashMap<DatapathId, TableId>(); + + if (json == null || json.isEmpty()) { + return retValue; + } + + try { + try { + jp = f.createJsonParser(json); + } 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; + } + + DatapathId dpid; + try { + n = n.trim(); + dpid = DatapathId.of(n); + + TableId tablesToGetDefaultFlow; + String value = jp.getText(); + if (value != null && !value.isEmpty()) { + value = value.trim().toLowerCase(); + try { + tablesToGetDefaultFlow = TableId.of( + value.startsWith("0x") + ? Integer.parseInt(value.replaceFirst("0x", ""), 16) + : Integer.parseInt(value) + ); /* will throw exception if outside valid TableId number range */ + retValue.put(dpid, tablesToGetDefaultFlow); + log.info("Setting max tables to receive table-miss flow to {} for DPID {}", + tablesToGetDefaultFlow.toString(), dpid.toString()); + } catch (IllegalArgumentException e) { /* catches both IllegalArgumentExcpt. and NumberFormatExcpt. */ + log.error("Invalid value of {} for max tables to receive table-miss flow for DPID {}. Using default of {}.", value, dpid.toString()); + } + } + } catch (NumberFormatException e) { + log.error("Invalid DPID format {} for max tables to receive table-miss flow for specific DPID. Using default for the intended DPID.", n); + } + } + } catch (IOException e) { + log.error("Using default for remaining DPIDs. JSON formatting error in max tables to receive table-miss flow for DPID input String: {}", e); + } + return retValue; } @Override @@ -757,10 +1010,10 @@ public class OFSwitchManager implements IOFSwitchManager, INewOFConnectionListen bootstrap.setOption("child.keepAlive", true); bootstrap.setOption("child.tcpNoDelay", true); bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE); - - ChannelPipelineFactory pfact = useSsl ? new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, keyStore, keyStorePassword) : - new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService); - + + ChannelPipelineFactory pfact = useSsl ? new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory, keyStore, keyStorePassword) : + new OpenflowPipelineFactory(this, floodlightProvider.getTimer(), this, debugCounterService, ofBitmaps, defaultFactory); + bootstrap.setPipelineFactory(pfact); InetSocketAddress sa = new InetSocketAddress(floodlightProvider.getOFPort()); final ChannelGroup cg = new DefaultChannelGroup(); diff --git a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java index 7dbcbf32eb6869671a4859fed538986016ef1092..11827d02fb50a490c7661179bab38e9b2b46292a 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java +++ b/src/main/java/net/floodlightcontroller/core/internal/OpenflowPipelineFactory.java @@ -19,6 +19,7 @@ package net.floodlightcontroller.core.internal; import java.io.FileInputStream; import java.security.KeyStore; +import java.util.List; import javax.annotation.Nonnull; import javax.net.ssl.KeyManager; @@ -36,6 +37,8 @@ import org.jboss.netty.handler.timeout.IdleStateHandler; import org.jboss.netty.handler.timeout.ReadTimeoutHandler; import org.jboss.netty.util.ExternalResourceReleasable; import org.jboss.netty.util.Timer; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.types.U32; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,14 +59,20 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { protected IDebugCounterService debugCounters; private String keyStore; private String keyStorePassword; + private OFFactory defaultFactory; + private List<U32> ofBitmaps; private void init(IOFSwitchManager switchManager, Timer timer, INewOFConnectionListener connectionListener, - IDebugCounterService debugCounters) { + IDebugCounterService debugCounters, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory) { this.switchManager = switchManager; this.connectionListener = connectionListener; this.timer = timer; this.debugCounters = debugCounters; + this.defaultFactory = defaultFactory; + this.ofBitmaps = ofBitmaps; this.idleHandler = new IdleStateHandler( timer, PipelineIdleReadTimeout.MAIN, @@ -74,9 +83,11 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer, INewOFConnectionListener connectionListener, - IDebugCounterService debugCounters) { + IDebugCounterService debugCounters, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory) { super(); - init(switchManager,timer, connectionListener, debugCounters); + init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory); this.keyStore = null; this.keyStorePassword = null; } @@ -84,9 +95,11 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { public OpenflowPipelineFactory(IOFSwitchManager switchManager, Timer timer, INewOFConnectionListener connectionListener, IDebugCounterService debugCounters, + @Nonnull List<U32> ofBitmaps, + @Nonnull OFFactory defaultFactory, @Nonnull String keyStore, @Nonnull String keyStorePassword) { super(); - init(switchManager,timer, connectionListener, debugCounters); + init(switchManager,timer, connectionListener, debugCounters, ofBitmaps, defaultFactory); this.keyStore = keyStore; this.keyStorePassword = keyStorePassword; } @@ -98,7 +111,9 @@ implements ChannelPipelineFactory, ExternalResourceReleasable { connectionListener, pipeline, debugCounters, - timer); + timer, + ofBitmaps, + defaultFactory); if (keyStore != null && keyStorePassword != null) { try { diff --git a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java index 03a46f462b68300c61639052b03a7931bfcc4861..7cb96bcee1f84545bcf4069d4aba091503d73cce 100644 --- a/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java +++ b/src/main/java/net/floodlightcontroller/core/rest/SwitchRepresentation.java @@ -47,7 +47,7 @@ public class SwitchRepresentation { // IOFSwitch this.buffers = sw.getBuffers(); this.capabilities = sw.getCapabilities(); - this.tables = sw.getTables(); + this.tables = sw.getNumTables(); this.inetAddress = sw.getInetAddress(); this.sortedPorts = sw.getSortedPorts(); this.isConnected = sw.isConnected(); diff --git a/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java b/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java index ed7da2dff8d2ee9d33c7121a9a17ca651deca78d..31a5e525a1e0b85ad11b3e608e13a950d929bd86 100644 --- a/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java +++ b/src/main/java/net/floodlightcontroller/core/types/MacVlanPair.java @@ -44,4 +44,8 @@ public class MacVlanPair { public int hashCode() { return mac.hashCode() ^ vlan.hashCode(); } -} \ No newline at end of file + + public String toString() { + return "(" + mac.toString() + ", " + vlan.toString() + ")"; + } +} diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java index 18b097db1e6fd1332459689375e20e7ffdc81f2a..c64f1a821be38a7c7fa49f1c7f21c51f106e6185 100644 --- a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java @@ -16,16 +16,13 @@ package net.floodlightcontroller.core.web; -import java.io.IOException; import java.util.HashMap; import java.util.Map; -import org.restlet.data.Status; import org.restlet.resource.ServerResource; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.RoleInfo; -import net.floodlightcontroller.core.HARole; import net.floodlightcontroller.core.annotations.LogMessageDoc; import org.restlet.resource.Get; @@ -34,7 +31,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.MappingJsonFactory; public class ControllerRoleResource extends ServerResource { diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java index 753685a358b7a5de190694a032327a7b362c216c..d67307676a9d2aa0386f7d4e32d4dee2fd0e644a 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/IOFSwitchSerializer.java @@ -60,7 +60,7 @@ public class IOFSwitchSerializer extends JsonSerializer<IOFSwitch> { serializePorts(sw.getPorts(),jGen); jGen.writeNumberField("buffers",sw.getBuffers()); jGen.writeStringField("inetAddress",sw.getInetAddress().toString()); - jGen.writeNumberField("tables",sw.getTables()); + jGen.writeNumberField("tables",sw.getNumTables()); jGen.writeNumberField("connectedSince",sw.getConnectedSince().getTime()); jGen.writeEndObject(); } diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/IPv6Serializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/IPv6Serializer.java new file mode 100644 index 0000000000000000000000000000000000000000..1394f53de55461ceed1836d30daf27f6f9c54332 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/IPv6Serializer.java @@ -0,0 +1,39 @@ +/** +* Copyright 2011,2012 Big Switch Networks, Inc. +* Originally created by David Erickson, Stanford University +* +* Licensed under the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. You may obtain +* a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +**/ + +package net.floodlightcontroller.core.web.serializers; + +import java.io.IOException; + +import org.projectfloodlight.openflow.types.IPv6Address; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +/** + * Serialize an IPv6Address in dotted decimal format + */ +public class IPv6Serializer extends JsonSerializer<IPv6Address> { + + @Override + public void serialize(IPv6Address ipv6, JsonGenerator jGen, + SerializerProvider serializer) + throws IOException, JsonProcessingException { + jGen.writeString(ipv6.toString()); + } +} diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java index 882a625c45cd3e8d4f7e860a42db8bb032a9b086..e0cbf88e0f94e4c6efdefc08c85a7d9ee6ba538f 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OFActionListSerializer.java @@ -41,7 +41,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthSrc; import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Code; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv4Type; - import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Code; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIcmpv6Type; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpDscp; @@ -49,7 +48,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpEcn; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpProto; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Dst; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv4Src; - import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Dst; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6Flabel; import org.projectfloodlight.openflow.protocol.oxm.OFOxmIpv6NdSll; @@ -105,190 +103,199 @@ public class OFActionListSerializer extends JsonSerializer<List<OFAction>> { * @throws JsonProcessingException */ public static void serializeActions(JsonGenerator jsonGenerator, List<OFAction> actions) throws IOException, JsonProcessingException { - if (actions.isEmpty()) { - jsonGenerator.writeStringField("none", "drop"); - } + StringBuilder sb = new StringBuilder(); + int len = actions.size(); + int pos = 0; for (OFAction a : actions) { switch (a.getType()) { case OUTPUT: - jsonGenerator.writeStringField(ActionUtils.STR_OUTPUT, ((OFActionOutput)a).getPort().toString()); + sb.append(ActionUtils.STR_OUTPUT).append("=").append(((OFActionOutput)a).getPort().toString()); break; /* begin OF1.0 ONLY actions */ case SET_VLAN_VID: - jsonGenerator.writeNumberField(ActionUtils.STR_VLAN_SET_VID, ((OFActionSetVlanVid)a).getVlanVid().getVlan()); + sb.append(ActionUtils.STR_VLAN_SET_VID).append("=").append(((OFActionSetVlanVid)a).getVlanVid().getVlan()); break; case SET_VLAN_PCP: - jsonGenerator.writeNumberField(ActionUtils.STR_VLAN_SET_PCP, ((OFActionSetVlanPcp)a).getVlanPcp().getValue()); + sb.append(ActionUtils.STR_VLAN_SET_PCP).append("=").append(((OFActionSetVlanPcp)a).getVlanPcp().getValue()); break; case SET_QUEUE: - jsonGenerator.writeNumberField(ActionUtils.STR_QUEUE_SET, ((OFActionSetQueue)a).getQueueId()); + sb.append(ActionUtils.STR_QUEUE_SET).append("=").append(((OFActionSetQueue)a).getQueueId()); break; case SET_DL_SRC: - jsonGenerator.writeStringField(ActionUtils.STR_DL_SRC_SET, ((OFActionSetDlSrc)a).getDlAddr().toString()); + sb.append(ActionUtils.STR_DL_SRC_SET).append("=").append(((OFActionSetDlSrc)a).getDlAddr().toString()); break; case SET_DL_DST: - jsonGenerator.writeStringField(ActionUtils.STR_DL_DST_SET, ((OFActionSetDlDst)a).getDlAddr().toString()); + sb.append(ActionUtils.STR_DL_DST_SET).append("=").append(((OFActionSetDlDst)a).getDlAddr().toString()); break; case SET_NW_SRC: - jsonGenerator.writeStringField(ActionUtils.STR_NW_SRC_SET, ((OFActionSetNwSrc)a).getNwAddr().toString()); + sb.append(ActionUtils.STR_NW_SRC_SET).append("=").append(((OFActionSetNwSrc)a).getNwAddr().toString()); break; case SET_NW_DST: - jsonGenerator.writeStringField(ActionUtils.STR_NW_DST_SET, ((OFActionSetNwDst)a).getNwAddr().toString()); + sb.append(ActionUtils.STR_NW_DST_SET).append("=").append(((OFActionSetNwDst)a).getNwAddr().toString()); break; case SET_NW_TOS: - jsonGenerator.writeNumberField(ActionUtils.STR_NW_TOS_SET, ((OFActionSetNwTos)a).getNwTos()); + sb.append(ActionUtils.STR_NW_TOS_SET).append("=").append(((OFActionSetNwTos)a).getNwTos()); break; case SET_TP_SRC: - jsonGenerator.writeNumberField(ActionUtils.STR_TP_SRC_SET, ((OFActionSetTpSrc)a).getTpPort().getPort()); + sb.append(ActionUtils.STR_TP_SRC_SET).append("=").append(((OFActionSetTpSrc)a).getTpPort().getPort()); break; case SET_TP_DST: - jsonGenerator.writeNumberField(ActionUtils.STR_TP_DST_SET, ((OFActionSetTpDst)a).getTpPort().getPort()); + sb.append(ActionUtils.STR_TP_DST_SET).append("=").append(((OFActionSetTpDst)a).getTpPort().getPort()); break; /* end OF1.0 ONLY actions; begin OF1.1+ actions */ case ENQUEUE: - jsonGenerator.writeNumberField(ActionUtils.STR_ENQUEUE, ((OFActionEnqueue)a).getPort().getPortNumber()); + sb.append(ActionUtils.STR_ENQUEUE).append("=").append(((OFActionEnqueue)a).getPort().getPortNumber()); break; case GROUP: - jsonGenerator.writeStringField(ActionUtils.STR_GROUP, ((OFActionGroup)a).getGroup().toString()); + sb.append(ActionUtils.STR_GROUP).append("=").append(((OFActionGroup)a).getGroup().toString()); break; case STRIP_VLAN: - jsonGenerator.writeStringField(ActionUtils.STR_VLAN_STRIP, ActionUtils.STR_NOT_APPLICABLE); + sb.append(ActionUtils.STR_VLAN_STRIP).append("=").append(ActionUtils.STR_NOT_APPLICABLE); break; case PUSH_VLAN: - jsonGenerator.writeNumberField(ActionUtils.STR_VLAN_PUSH, ((OFActionPushVlan)a).getEthertype().getValue()); + sb.append(ActionUtils.STR_VLAN_PUSH).append("=").append(((OFActionPushVlan)a).getEthertype().getValue()); break; case PUSH_MPLS: - jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_PUSH, ((OFActionPushMpls)a).getEthertype().getValue()); + sb.append(ActionUtils.STR_MPLS_PUSH).append("=").append(((OFActionPushMpls)a).getEthertype().getValue()); break; case PUSH_PBB: - jsonGenerator.writeNumberField(ActionUtils.STR_PBB_PUSH, ((OFActionPushPbb)a).getEthertype().getValue()); + sb.append(ActionUtils.STR_PBB_PUSH).append("=").append(((OFActionPushPbb)a).getEthertype().getValue()); break; case POP_VLAN: - jsonGenerator.writeStringField(ActionUtils.STR_VLAN_POP, ActionUtils.STR_NOT_APPLICABLE); + sb.append(ActionUtils.STR_VLAN_POP).append("=").append(ActionUtils.STR_NOT_APPLICABLE); break; case POP_MPLS: - jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_POP, ((OFActionPopMpls)a).getEthertype().getValue()); + sb.append(ActionUtils.STR_MPLS_POP).append("=").append(((OFActionPopMpls)a).getEthertype().getValue()); break; case POP_PBB: - jsonGenerator.writeStringField(ActionUtils.STR_PBB_POP, ActionUtils.STR_NOT_APPLICABLE); + sb.append(ActionUtils.STR_PBB_POP).append("=").append(ActionUtils.STR_NOT_APPLICABLE); break; case COPY_TTL_IN: - jsonGenerator.writeStringField(ActionUtils.STR_TTL_IN_COPY, ActionUtils.STR_NOT_APPLICABLE); + sb.append(ActionUtils.STR_TTL_IN_COPY).append("=").append(ActionUtils.STR_NOT_APPLICABLE); break; case COPY_TTL_OUT: - jsonGenerator.writeStringField(ActionUtils.STR_TTL_OUT_COPY, ActionUtils.STR_NOT_APPLICABLE); + sb.append(ActionUtils.STR_TTL_OUT_COPY).append("=").append(ActionUtils.STR_NOT_APPLICABLE); break; case DEC_NW_TTL: - jsonGenerator.writeStringField(ActionUtils.STR_NW_TTL_DEC, ActionUtils.STR_NOT_APPLICABLE); + sb.append(ActionUtils.STR_NW_TTL_DEC).append("=").append(ActionUtils.STR_NOT_APPLICABLE); break; case DEC_MPLS_TTL: - jsonGenerator.writeStringField(ActionUtils.STR_MPLS_TTL_DEC, ActionUtils.STR_NOT_APPLICABLE); + sb.append(ActionUtils.STR_MPLS_TTL_DEC).append("=").append(ActionUtils.STR_NOT_APPLICABLE); break; case SET_MPLS_LABEL: - jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_LABEL_SET, ((OFActionSetMplsLabel)a).getMplsLabel()); + sb.append(ActionUtils.STR_MPLS_LABEL_SET).append("=").append(((OFActionSetMplsLabel)a).getMplsLabel()); break; case SET_MPLS_TC: - jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_TC_SET, ((OFActionSetMplsTc)a).getMplsTc()); + sb.append(ActionUtils.STR_MPLS_TC_SET).append("=").append(((OFActionSetMplsTc)a).getMplsTc()); break; case SET_MPLS_TTL: - jsonGenerator.writeNumberField(ActionUtils.STR_MPLS_TTL_SET, ((OFActionSetMplsTtl)a).getMplsTtl()); + sb.append(ActionUtils.STR_MPLS_TTL_SET).append("=").append(((OFActionSetMplsTtl)a).getMplsTtl()); break; case SET_NW_ECN: - jsonGenerator.writeNumberField(ActionUtils.STR_NW_ECN_SET, ((OFActionSetNwEcn)a).getNwEcn().getEcnValue()); + sb.append(ActionUtils.STR_NW_ECN_SET).append("=").append(((OFActionSetNwEcn)a).getNwEcn().getEcnValue()); break; case SET_NW_TTL: - jsonGenerator.writeNumberField(ActionUtils.STR_NW_TTL_SET, ((OFActionSetNwTtl)a).getNwTtl()); + sb.append(ActionUtils.STR_NW_TTL_SET).append("=").append(((OFActionSetNwTtl)a).getNwTtl()); break; case EXPERIMENTER: - jsonGenerator.writeNumberField(ActionUtils.STR_EXPERIMENTER, ((OFActionExperimenter)a).getExperimenter()); + sb.append(ActionUtils.STR_EXPERIMENTER).append("=").append(((OFActionExperimenter)a).getExperimenter()); break; case SET_FIELD: if (((OFActionSetField)a).getField() instanceof OFOxmArpOp) { - jsonGenerator.writeNumberField(MatchUtils.STR_ARP_OPCODE, ((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode()); + sb.append(MatchUtils.STR_ARP_OPCODE).append("=").append(((OFOxmArpOp) ((OFActionSetField) a).getField()).getValue().getOpcode()); } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSha) { - jsonGenerator.writeStringField(MatchUtils.STR_ARP_SHA, ((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already + sb.append(MatchUtils.STR_ARP_SHA).append("=").append(((OFOxmArpSha) ((OFActionSetField) a).getField()).getValue().toString()); // macaddress formats string already } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTha) { - jsonGenerator.writeStringField(MatchUtils.STR_ARP_DHA, ((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_ARP_DHA).append("=").append(((OFOxmArpTha) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmArpSpa) { - jsonGenerator.writeStringField(MatchUtils.STR_ARP_SPA, ((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already + sb.append(MatchUtils.STR_ARP_SPA).append("=").append(((OFOxmArpSpa) ((OFActionSetField) a).getField()).getValue().toString()); // ipaddress formats string already } else if (((OFActionSetField)a).getField() instanceof OFOxmArpTpa) { - jsonGenerator.writeStringField(MatchUtils.STR_ARP_DPA, ((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_ARP_DPA).append("=").append(((OFOxmArpTpa) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdSll) { - jsonGenerator.writeStringField(MatchUtils.STR_IPV6_ND_SSL, ((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_IPV6_ND_SSL).append("=").append(((OFOxmIpv6NdSll) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTll) { - jsonGenerator.writeStringField(MatchUtils.STR_IPV6_ND_TTL, ((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_IPV6_ND_TTL).append("=").append(((OFOxmIpv6NdTll) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6NdTarget) { - jsonGenerator.writeStringField(MatchUtils.STR_IPV6_ND_TARGET, ((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_IPV6_ND_TARGET).append("=").append(((OFOxmIpv6NdTarget) ((OFActionSetField) a).getField()).getValue().toString()); } /* DATA LAYER */ else if (((OFActionSetField)a).getField() instanceof OFOxmEthType) { - jsonGenerator.writeNumberField(MatchUtils.STR_DL_TYPE, ((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue()); + sb.append(MatchUtils.STR_DL_TYPE).append("=").append(((OFOxmEthType) ((OFActionSetField) a).getField()).getValue().getValue()); } else if (((OFActionSetField)a).getField() instanceof OFOxmEthSrc) { - jsonGenerator.writeStringField(MatchUtils.STR_DL_SRC, ((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_DL_SRC).append("=").append(((OFOxmEthSrc) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmEthDst) { - jsonGenerator.writeStringField(MatchUtils.STR_DL_DST, ((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_DL_DST).append("=").append(((OFOxmEthDst) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanVid) { - jsonGenerator.writeNumberField(MatchUtils.STR_DL_VLAN, ((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan()); + sb.append(MatchUtils.STR_DL_VLAN).append("=").append(((OFOxmVlanVid) ((OFActionSetField) a).getField()).getValue().getVlan()); } else if (((OFActionSetField)a).getField() instanceof OFOxmVlanPcp) { } /* ICMP */ else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Code) { - jsonGenerator.writeNumberField(MatchUtils.STR_ICMP_CODE, ((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode()); + sb.append(MatchUtils.STR_ICMP_CODE).append("=").append(((OFOxmIcmpv4Code) ((OFActionSetField) a).getField()).getValue().getCode()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv4Type) { - jsonGenerator.writeNumberField(MatchUtils.STR_ICMP_TYPE, ((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType()); + sb.append(MatchUtils.STR_ICMP_TYPE).append("=").append(((OFOxmIcmpv4Type) ((OFActionSetField) a).getField()).getValue().getType()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Code) { - jsonGenerator.writeNumberField(MatchUtils.STR_ICMPV6_CODE, ((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw()); + sb.append(MatchUtils.STR_ICMPV6_CODE).append("=").append(((OFOxmIcmpv6Code) ((OFActionSetField) a).getField()).getValue().getRaw()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIcmpv6Type) { - jsonGenerator.writeNumberField(MatchUtils.STR_ICMPV6_TYPE, ((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw()); + sb.append(MatchUtils.STR_ICMPV6_TYPE).append("=").append(((OFOxmIcmpv6Type) ((OFActionSetField) a).getField()).getValue().getRaw()); } /* NETWORK LAYER */ else if (((OFActionSetField)a).getField() instanceof OFOxmIpProto) { - jsonGenerator.writeNumberField(MatchUtils.STR_NW_PROTO, ((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber()); + sb.append(MatchUtils.STR_NW_PROTO).append("=").append(((OFOxmIpProto) ((OFActionSetField) a).getField()).getValue().getIpProtocolNumber()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Src) { - jsonGenerator.writeStringField(MatchUtils.STR_NW_SRC, ((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_NW_SRC).append("=").append(((OFOxmIpv4Src) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv4Dst) { - jsonGenerator.writeStringField(MatchUtils.STR_NW_DST, ((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_NW_DST).append("=").append(((OFOxmIpv4Dst) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Src) { - jsonGenerator.writeStringField(MatchUtils.STR_IPV6_SRC, ((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_IPV6_SRC).append("=").append(((OFOxmIpv6Src) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Dst) { - jsonGenerator.writeStringField(MatchUtils.STR_IPV6_DST, ((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_IPV6_DST).append("=").append(((OFOxmIpv6Dst) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpv6Flabel) { - jsonGenerator.writeStringField(MatchUtils.STR_IPV6_FLOW_LABEL, ((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_IPV6_FLOW_LABEL).append("=").append(((OFOxmIpv6Flabel) ((OFActionSetField) a).getField()).getValue().toString()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpEcn) { - jsonGenerator.writeNumberField(MatchUtils.STR_NW_ECN, ((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue()); + sb.append(MatchUtils.STR_NW_ECN).append("=").append(((OFOxmIpEcn) ((OFActionSetField) a).getField()).getValue().getEcnValue()); } else if (((OFActionSetField)a).getField() instanceof OFOxmIpDscp) { - jsonGenerator.writeNumberField(MatchUtils.STR_NW_DSCP, ((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue()); + sb.append(MatchUtils.STR_NW_DSCP).append("=").append(((OFOxmIpDscp) ((OFActionSetField) a).getField()).getValue().getDscpValue()); } /* TRANSPORT LAYER, TCP, UDP, and SCTP */ else if (((OFActionSetField)a).getField() instanceof OFOxmTcpSrc) { - jsonGenerator.writeNumberField(MatchUtils.STR_TCP_SRC, ((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); + sb.append(MatchUtils.STR_TCP_SRC).append("=").append(((OFOxmTcpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); } else if (((OFActionSetField)a).getField() instanceof OFOxmTcpDst) { - jsonGenerator.writeNumberField(MatchUtils.STR_TCP_DST, ((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort()); + sb.append(MatchUtils.STR_TCP_DST).append("=").append(((OFOxmTcpDst) ((OFActionSetField) a).getField()).getValue().getPort()); } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpSrc) { - jsonGenerator.writeNumberField(MatchUtils.STR_UDP_SRC, ((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); + sb.append(MatchUtils.STR_UDP_SRC).append("=").append(((OFOxmUdpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); } else if (((OFActionSetField)a).getField() instanceof OFOxmUdpDst) { - jsonGenerator.writeNumberField(MatchUtils.STR_UDP_DST, ((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort()); + sb.append(MatchUtils.STR_UDP_DST).append("=").append(((OFOxmUdpDst) ((OFActionSetField) a).getField()).getValue().getPort()); } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpSrc) { - jsonGenerator.writeNumberField(MatchUtils.STR_SCTP_SRC, ((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); + sb.append(MatchUtils.STR_SCTP_SRC).append("=").append(((OFOxmSctpSrc) ((OFActionSetField) a).getField()).getValue().getPort()); } else if (((OFActionSetField)a).getField() instanceof OFOxmSctpDst) { - jsonGenerator.writeNumberField(MatchUtils.STR_SCTP_DST, ((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort()); + sb.append(MatchUtils.STR_SCTP_DST).append("=").append(((OFOxmSctpDst) ((OFActionSetField) a).getField()).getValue().getPort()); } /* MPLS */ else if (((OFActionSetField)a).getField() instanceof OFOxmMplsLabel) { - jsonGenerator.writeNumberField(MatchUtils.STR_MPLS_LABEL, ((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue()); + sb.append(MatchUtils.STR_MPLS_LABEL).append("=").append(((OFOxmMplsLabel) ((OFActionSetField) a).getField()).getValue().getValue()); } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsTc) { - jsonGenerator.writeNumberField(MatchUtils.STR_MPLS_TC, ((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue()); + sb.append(MatchUtils.STR_MPLS_TC).append("=").append(((OFOxmMplsTc) ((OFActionSetField) a).getField()).getValue().getValue()); } else if (((OFActionSetField)a).getField() instanceof OFOxmMplsBos) { - jsonGenerator.writeStringField(MatchUtils.STR_MPLS_TC, ((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().toString()); + sb.append(MatchUtils.STR_MPLS_TC).append("=").append(((OFOxmMplsBos) ((OFActionSetField) a).getField()).getValue().toString()); } /* METADATA */ else if (((OFActionSetField)a).getField() instanceof OFOxmMetadata) { - jsonGenerator.writeNumberField(MatchUtils.STR_METADATA, ((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue()); + sb.append(MatchUtils.STR_METADATA).append("=").append(((OFOxmMetadata) ((OFActionSetField) a).getField()).getValue().getValue().getValue()); } else { logger.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a)); - // need to get a logger in here somehow log.error("Could not decode Set-Field action field: {}", ((OFActionSetField) a)); } } // end switch over action type + pos++; + if (pos < len) { + sb.append(","); + } } // end for over all actions + + if (actions.isEmpty()) { + jsonGenerator.writeStringField("none", "drop"); + } else { + jsonGenerator.writeStringField("actions", sb.toString()); + } } // end method } diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..c0b13916edfe037ab43e298f68be93fe18279967 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/OXMSerializer.java @@ -0,0 +1,440 @@ +package net.floodlightcontroller.core.web.serializers; + +import net.floodlightcontroller.util.MatchUtils; + +import org.projectfloodlight.openflow.protocol.ver14.OFOxmClassSerializerVer14; +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.IPv6Address; +import org.projectfloodlight.openflow.types.IPv6FlowLabel; +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.OFBooleanValue; +import org.projectfloodlight.openflow.types.OFMetadata; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.projectfloodlight.openflow.types.VlanVid; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +/** + * A handy class to convert OXMs (and NXMs) to and from + * their IDs (U32=[class]+[field]+[has_mask]+[length]) + * and their string representations. The strings are + * defined in {@link net.floodlightcontroller.util.MatchUtils}, + * with the exception of some unique NXMs and OpenFlow 1.5 OXMs + * not technically supported by Loxi/Floodlight yet. Any NXM + * string is prefixed with an "nxm_" followed by the string + * used for the corresponding OXM. For example, an ingress port + * OXM is "in_port", while the NXM version is "nxm_in_port". + * + * Any unique NXM without an OXM counterpart is assigned a string + * to inform of the NXM. For example, NXM 33, "nxm_pkt_mark", marks + * or matches on marked packets as Linux IP tables can do. This is + * not supported in the OpenFlow spec, thus the NXM is assigned the + * string "nxm_pkt_mark" within this class and does not have a string + * predefined in {@link net.floodlightcontroller.util.MatchUtils}. + * + * Any OXM with the HAS_MASKED bit set will have it's string + * representation appended with an "_masked" or the string defined in + * the STR_MASKED class variable below. + * + * As an implementation note, a Google BiMap is used as the storage + * mechanism. This allows fast lookup in both directions from U32 to + * String or from String back to U32. Note that unique keys AND values + * are required in a BiMap. + * + * @author Ryan Izard, rizard@g.clemson.edu, ryan.izard@bigswitch.com + * + */ +public class OXMSerializer { + + /* + * OXM IDs for OpenFlow versions 1.5 and below are the same, + * where each version simply adds to the already populated + * list of OXM IDs. (IDs don't change b/t OF versions.) + * As such, we'll assume OF1.4 here, which includes 1.3. + * + * TODO Later, when Loxi supports 1.5, we'll change to + * OFOxmClassSerializerVer15. + */ + + /* String constants */ + private static final String STR_MASKED = "_masked"; + private static final String STR_NXM = "nxm_"; + + /* Bit shifting constants */ + private static final int SHIFT_FIELD = 9; + private static final int SHIFT_CLASS = 16; + private static final int SHIFT_HAS_MASK = 8; + + /* Mask present bit */ + private static final int MASKED = (1 << SHIFT_HAS_MASK); + + /* Classes */ + private static final int OF_BASIC = (OFOxmClassSerializerVer14.OPENFLOW_BASIC_VAL << SHIFT_CLASS) & 0xFFffFFff; + //TODO private static final int OF_PKT_REG = (0x8001 << SHIFT_CLASS) & 0xFFffFFff; + private static final int NXM_0 = (OFOxmClassSerializerVer14.NXM_0_VAL << SHIFT_CLASS) & 0xFFffFFff; + private static final int NXM_1 = (OFOxmClassSerializerVer14.NXM_1_VAL << SHIFT_CLASS) & 0xFFffFFff; + + /* The bi-directional map we'll use to make this process more efficient */ + private static final BiMap<U32, String> map = HashBiMap.create(); + static { + /* + * OXM header is 4 bytes as follows, where C=oxm_class, + * F=oxm_field, M=has_mask, and L=data_length. + * + * CCCC CCCC CCCC CCCC FFFF FFFM LLLL LLLL + * + * The OXM class has already been shifted to the proper + * position and is represented as an integer (4 bytes), + * but we need to shift the OXM field over. The length + * does not need to be shifted. + * + */ + + /* + * OpenFlow Basic OXM Definitions + */ + map.put(U32.ofRaw(OF_BASIC | (0 << SHIFT_FIELD) | OFPort.ZERO.getLength()), MatchUtils.STR_IN_PORT); + map.put(U32.ofRaw(OF_BASIC | (0 << SHIFT_FIELD) | OFPort.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_IN_PORT + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (1 << SHIFT_FIELD) | OFPort.ZERO.getLength()), MatchUtils.STR_IN_PHYS_PORT); + map.put(U32.ofRaw(OF_BASIC | (1 << SHIFT_FIELD) | OFPort.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_IN_PHYS_PORT + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (2 << SHIFT_FIELD) | OFMetadata.NONE.getLength()), MatchUtils.STR_METADATA); + map.put(U32.ofRaw(OF_BASIC | (2 << SHIFT_FIELD) | OFMetadata.NONE.getLength() * 2 | MASKED), MatchUtils.STR_METADATA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (3 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_DL_DST); + map.put(U32.ofRaw(OF_BASIC | (3 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (4 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_DL_SRC); + map.put(U32.ofRaw(OF_BASIC | (4 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (5 << SHIFT_FIELD) | EthType.NONE.getLength()), MatchUtils.STR_DL_TYPE); + map.put(U32.ofRaw(OF_BASIC | (5 << SHIFT_FIELD) | EthType.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_TYPE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (6 << SHIFT_FIELD) | VlanVid.ZERO.getLength()), MatchUtils.STR_DL_VLAN); + map.put(U32.ofRaw(OF_BASIC | (6 << SHIFT_FIELD) | VlanVid.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_DL_VLAN + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (7 << SHIFT_FIELD) | VlanPcp.NONE.getLength()), MatchUtils.STR_DL_VLAN_PCP); + map.put(U32.ofRaw(OF_BASIC | (7 << SHIFT_FIELD) | VlanPcp.NONE.getLength() * 2 | MASKED), MatchUtils.STR_DL_VLAN_PCP + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (8 << SHIFT_FIELD) | IpDscp.NONE.getLength()), MatchUtils.STR_NW_DSCP); + map.put(U32.ofRaw(OF_BASIC | (8 << SHIFT_FIELD) | IpDscp.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_DSCP + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (9 << SHIFT_FIELD) | IpEcn.NONE.getLength()), MatchUtils.STR_NW_ECN); + map.put(U32.ofRaw(OF_BASIC | (9 << SHIFT_FIELD) | IpEcn.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_ECN + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (10 << SHIFT_FIELD) | IpProtocol.NONE.getLength()), MatchUtils.STR_NW_PROTO); + map.put(U32.ofRaw(OF_BASIC | (10 << SHIFT_FIELD) | IpProtocol.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_PROTO + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (11 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_NW_SRC); + map.put(U32.ofRaw(OF_BASIC | (11 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (12 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_NW_DST); + map.put(U32.ofRaw(OF_BASIC | (12 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_NW_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (13 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_TCP_SRC); + map.put(U32.ofRaw(OF_BASIC | (13 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TCP_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (14 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_TCP_DST); + map.put(U32.ofRaw(OF_BASIC | (14 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TCP_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (15 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_UDP_SRC); + map.put(U32.ofRaw(OF_BASIC | (15 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_UDP_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (16 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_UDP_DST); + map.put(U32.ofRaw(OF_BASIC | (16 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_UDP_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (17 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_SCTP_SRC); + map.put(U32.ofRaw(OF_BASIC | (17 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_SCTP_SRC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (18 << SHIFT_FIELD) | TransportPort.NONE.getLength()), MatchUtils.STR_SCTP_DST); + map.put(U32.ofRaw(OF_BASIC | (18 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), MatchUtils.STR_SCTP_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (19 << SHIFT_FIELD) | ICMPv4Type.NONE.getLength()), MatchUtils.STR_ICMP_TYPE); + map.put(U32.ofRaw(OF_BASIC | (19 << SHIFT_FIELD) | ICMPv4Type.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ICMP_TYPE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (20 << SHIFT_FIELD) | ICMPv4Code.NONE.getLength()), MatchUtils.STR_ICMP_CODE); + map.put(U32.ofRaw(OF_BASIC | (20 << SHIFT_FIELD) | ICMPv4Code.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ICMP_CODE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (21 << SHIFT_FIELD) | ArpOpcode.NONE.getLength()), MatchUtils.STR_ARP_OPCODE); + map.put(U32.ofRaw(OF_BASIC | (21 << SHIFT_FIELD) | ArpOpcode.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_OPCODE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (22 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_ARP_SPA); + map.put(U32.ofRaw(OF_BASIC | (22 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_SPA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (23 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_ARP_DPA); + map.put(U32.ofRaw(OF_BASIC | (23 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_DPA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_ARP_SHA); + map.put(U32.ofRaw(OF_BASIC | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_SHA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_ARP_DHA); + map.put(U32.ofRaw(OF_BASIC | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_ARP_DHA + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_SRC); + map.put(U32.ofRaw(OF_BASIC | (26 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_SRC + STR_MASKED); + + + map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_DST); + map.put(U32.ofRaw(OF_BASIC | (27 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_DST + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (28 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength()), MatchUtils.STR_IPV6_FLOW_LABEL); + map.put(U32.ofRaw(OF_BASIC | (28 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_FLOW_LABEL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (29 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_ICMPV6_TYPE); + map.put(U32.ofRaw(OF_BASIC | (29 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_ICMPV6_TYPE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (30 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_ICMPV6_CODE); + map.put(U32.ofRaw(OF_BASIC | (30 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_ICMPV6_CODE + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), MatchUtils.STR_IPV6_ND_TARGET); + map.put(U32.ofRaw(OF_BASIC | (31 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_SSL); + map.put(U32.ofRaw(OF_BASIC | (32 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_SSL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength()), MatchUtils.STR_IPV6_ND_TTL); + map.put(U32.ofRaw(OF_BASIC | (33 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), MatchUtils.STR_IPV6_ND_TTL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength()), MatchUtils.STR_MPLS_LABEL); + map.put(U32.ofRaw(OF_BASIC | (34 << SHIFT_FIELD) | U32.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_LABEL + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (35 << SHIFT_FIELD) | U8.ZERO.getLength()), MatchUtils.STR_MPLS_TC); + map.put(U32.ofRaw(OF_BASIC | (35 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_TC + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength()), MatchUtils.STR_MPLS_BOS); + map.put(U32.ofRaw(OF_BASIC | (36 << SHIFT_FIELD) | OFBooleanValue.TRUE.getLength() * 2 | MASKED), MatchUtils.STR_MPLS_BOS + STR_MASKED); + + /* + * TODO in Loxi and MatchUtils + * map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | ), MatchUtils.STR_PBB_ISID); + */ + map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 ), MatchUtils.STR_PBB_ISID); /* need to update after Loxi support */ + map.put(U32.ofRaw(OF_BASIC | (37 << SHIFT_FIELD) | 3 * 2 | MASKED ), MatchUtils.STR_PBB_ISID + STR_MASKED); + + map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength()), MatchUtils.STR_TUNNEL_ID); + map.put(U32.ofRaw(OF_BASIC | (38 << SHIFT_FIELD) | U64.ZERO.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_ID + STR_MASKED); + + /* + * TODO in MatchUtils + * map.put(U32.ofRaw(OF_BASIC | 39), MatchUtils.STR_IPV6_EXTHDR); + */ + map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2), "ipv6_exthdr"); /* need to update after Loxi support */ + map.put(U32.ofRaw(OF_BASIC | (39 << SHIFT_FIELD) | 2 * 2 | MASKED), "ipv6_exthdr" + STR_MASKED); + + /* note skip of ID 40 here according to spec */ + + /* + * TODO in MatchUtils + * map.put(U32.ofRaw(OF_BASIC | 41), MatchUtils.STR_PBB_UCA); + */ + map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1), "pbb_uca"); + map.put(U32.ofRaw(OF_BASIC | (41 << SHIFT_FIELD) | 1 * 2 | MASKED), "pbb_uca" + STR_MASKED); + + /* + * NXM_0 Definitions + */ + map.put(U32.ofRaw(NXM_0 | (0 << SHIFT_FIELD) | 2 /* old ports are len=2 */), STR_NXM + MatchUtils.STR_IN_PORT); + map.put(U32.ofRaw(NXM_0 | (0 << SHIFT_FIELD) | 2 * 2 /* old ports are len=2 */ | MASKED), STR_NXM + MatchUtils.STR_IN_PORT + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (1 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_DST); + map.put(U32.ofRaw(NXM_0 | (1 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (2 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_SRC); + map.put(U32.ofRaw(NXM_0 | (2 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (3 << SHIFT_FIELD) | EthType.NONE.getLength()), STR_NXM + MatchUtils.STR_DL_TYPE); + map.put(U32.ofRaw(NXM_0 | (3 << SHIFT_FIELD) | EthType.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_TYPE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (4 << SHIFT_FIELD) | VlanVid.ZERO.getLength()), STR_NXM + MatchUtils.STR_DL_VLAN); + map.put(U32.ofRaw(NXM_0 | (4 << SHIFT_FIELD) | VlanVid.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_DL_VLAN + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (5 << SHIFT_FIELD) | 1), STR_NXM + MatchUtils.STR_NW_TOS); + map.put(U32.ofRaw(NXM_0 | (5 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_TOS + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (6 << SHIFT_FIELD) | IpProtocol.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_PROTO); + map.put(U32.ofRaw(NXM_0 | (6 << SHIFT_FIELD) | IpProtocol.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_PROTO + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (7 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_SRC); + map.put(U32.ofRaw(NXM_0 | (7 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (8 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_DST); + map.put(U32.ofRaw(NXM_0 | (8 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (9 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_TCP_SRC); + map.put(U32.ofRaw(NXM_0 | (9 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TCP_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (10 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_TCP_DST); + map.put(U32.ofRaw(NXM_0 | (10 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TCP_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (11 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_UDP_SRC); + map.put(U32.ofRaw(NXM_0 | (11 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_UDP_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (12 << SHIFT_FIELD) | TransportPort.NONE.getLength()), STR_NXM + MatchUtils.STR_UDP_DST); + map.put(U32.ofRaw(NXM_0 | (12 << SHIFT_FIELD) | TransportPort.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_UDP_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (13 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMP_TYPE); + map.put(U32.ofRaw(NXM_0 | (13 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMP_TYPE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (14 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMP_CODE); + map.put(U32.ofRaw(NXM_0 | (14 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMP_CODE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (15 << SHIFT_FIELD) | ArpOpcode.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_OPCODE); + map.put(U32.ofRaw(NXM_0 | (15 << SHIFT_FIELD) | ArpOpcode.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_OPCODE + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (16 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_SPA); + map.put(U32.ofRaw(NXM_0 | (16 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_SPA + STR_MASKED); + + map.put(U32.ofRaw(NXM_0 | (17 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_DPA); + map.put(U32.ofRaw(NXM_0 | (17 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_DPA + STR_MASKED); + + /* + * NXM_1 Definitions + */ + map.put(U32.ofRaw(NXM_1 | (0 << SHIFT_FIELD) | 4), STR_NXM + "reg_0"); + map.put(U32.ofRaw(NXM_1 | (0 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_0" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (1 << SHIFT_FIELD) | 4), STR_NXM + "reg_1"); + map.put(U32.ofRaw(NXM_1 | (1 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_1" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (2 << SHIFT_FIELD) | 4), STR_NXM + "reg_2"); + map.put(U32.ofRaw(NXM_1 | (2 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_2" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (3 << SHIFT_FIELD) | 4), STR_NXM + "reg_3"); + map.put(U32.ofRaw(NXM_1 | (3 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_3" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (4 << SHIFT_FIELD) | 4), STR_NXM + "reg_4"); + map.put(U32.ofRaw(NXM_1 | (4 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_4" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (5 << SHIFT_FIELD) | 4), STR_NXM + "reg_5"); + map.put(U32.ofRaw(NXM_1 | (5 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_5" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (6 << SHIFT_FIELD) | 4), STR_NXM + "reg_6"); + map.put(U32.ofRaw(NXM_1 | (6 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_6" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (7 << SHIFT_FIELD) | 4), STR_NXM + "reg_7"); + map.put(U32.ofRaw(NXM_1 | (7 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "reg_7" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (16 << SHIFT_FIELD) | U64.ZERO.getLength()), STR_NXM + MatchUtils.STR_TUNNEL_ID); + map.put(U32.ofRaw(NXM_1 | (16 << SHIFT_FIELD) | U64.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_TUNNEL_ID + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (17 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_SHA); + map.put(U32.ofRaw(NXM_1 | (17 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_SHA + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (18 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_ARP_DHA); + map.put(U32.ofRaw(NXM_1 | (18 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ARP_DHA + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (19 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_SRC); + map.put(U32.ofRaw(NXM_1 | (19 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_SRC + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (20 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_DST); + map.put(U32.ofRaw(NXM_1 | (20 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_DST + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (21 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMPV6_TYPE); + map.put(U32.ofRaw(NXM_1 | (21 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMPV6_TYPE + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (22 << SHIFT_FIELD) | U8.ZERO.getLength()), STR_NXM + MatchUtils.STR_ICMPV6_CODE); + map.put(U32.ofRaw(NXM_1 | (22 << SHIFT_FIELD) | U8.ZERO.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_ICMPV6_CODE + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET); + map.put(U32.ofRaw(NXM_1 | (23 << SHIFT_FIELD) | IPv6Address.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TARGET + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_SSL); + map.put(U32.ofRaw(NXM_1 | (24 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_SSL + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_ND_TTL); + map.put(U32.ofRaw(NXM_1 | (25 << SHIFT_FIELD) | MacAddress.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_ND_TTL + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1), STR_NXM + "ip_frag"); + map.put(U32.ofRaw(NXM_1 | (26 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + "ip_frag" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (27 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength()), STR_NXM + MatchUtils.STR_IPV6_FLOW_LABEL); + map.put(U32.ofRaw(NXM_1 | (27 << SHIFT_FIELD) | IPv6FlowLabel.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_IPV6_FLOW_LABEL + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (28 << SHIFT_FIELD) | IpEcn.NONE.getLength()), STR_NXM + MatchUtils.STR_NW_ECN); + map.put(U32.ofRaw(NXM_1 | (28 << SHIFT_FIELD) | IpEcn.NONE.getLength() * 2 | MASKED), STR_NXM + MatchUtils.STR_NW_ECN + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (29 << SHIFT_FIELD) | 1), STR_NXM + "ip_ttl"); + map.put(U32.ofRaw(NXM_1 | (29 << SHIFT_FIELD) | 1 * 2 | MASKED), STR_NXM + "ip_ttl" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (30 << SHIFT_FIELD) | 8), STR_NXM + "flow_cookie"); + map.put(U32.ofRaw(NXM_1 | (30 << SHIFT_FIELD) | 8 * 2 | MASKED), STR_NXM + "flow_cookie" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (31 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_TUNNEL_IPV4_SRC); + map.put(U32.ofRaw(NXM_1 | (31 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_IPV4_SRC + MASKED); + + map.put(U32.ofRaw(NXM_1 | (32 << SHIFT_FIELD) | IPv4Address.NONE.getLength()), MatchUtils.STR_TUNNEL_IPV4_DST); + map.put(U32.ofRaw(NXM_1 | (32 << SHIFT_FIELD) | IPv4Address.NONE.getLength() * 2 | MASKED), MatchUtils.STR_TUNNEL_IPV4_DST + MASKED); + + map.put(U32.ofRaw(NXM_1 | (33 << SHIFT_FIELD) | 4), STR_NXM + "pkt_mark"); + map.put(U32.ofRaw(NXM_1 | (33 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "pkt_mark" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (34 << SHIFT_FIELD) | 2), STR_NXM + "tcp_flags"); + map.put(U32.ofRaw(NXM_1 | (34 << SHIFT_FIELD) | 2 * 2 | MASKED), STR_NXM + "tcp_flags" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (35 << SHIFT_FIELD) | 4), STR_NXM + "internal_dp_hash"); + map.put(U32.ofRaw(NXM_1 | (35 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "internal_dp_hash" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (36 << SHIFT_FIELD) | 4), STR_NXM + "internal_recirc_id"); + map.put(U32.ofRaw(NXM_1 | (36 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + "internal_recirc_id" + STR_MASKED); + + map.put(U32.ofRaw(NXM_1 | (37 << SHIFT_FIELD) | 4), STR_NXM + MatchUtils.STR_PBB_ISID); + map.put(U32.ofRaw(NXM_1 | (37 << SHIFT_FIELD) | 4 * 2 | MASKED), STR_NXM + MatchUtils.STR_PBB_ISID + STR_MASKED); + + } + + /** + * Converts the string representation of an OXM ID as + * defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * to its corresponding U32 OXM ID. + * + * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * is the same as the OXM string with an "nxm_" prefix. + * + * Any OXM or NXM that is masked include a "_masked" postfix. + * + * @param oxmId, the U32 defining the OXM/NXM as [class]+[field]+[has_mask]+[length] + * @return the String representing the OXM/NXM + */ + public static String oxmIdToString(U32 oxmId) { + if (map.containsKey(oxmId)) { + return map.get(oxmId); + } else { + return "Unknown OXM ID: " + oxmId.toString(); + } + } + + /** + * Converts the string representation of an OXM ID as + * defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * to its corresponding U32 OXM ID. + * + * Any NXM (not defined in {@link net.floodlightcontroller.util.MatchUtils.java} + * is the same as the OXM string with an "nxm_" prefix. + * + * Any OXM or NXM that is masked include a "_masked" postfix. + * + * @param oxmString, the string definging the OXM/NXM + * @return the U32 representing the OXM/NXM as [class]+[field]+[has_mask]+[length] + */ + public static U32 oxmStringToId(String oxmString) { + if (map.inverse().containsKey(oxmString)) { + return map.inverse().get(oxmString); + } else { + return null; + } + } +} diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java index bd728ff80be82b0993fd10385b8dca53c63a0fed..3481e974505c4bcdfe50801a60a40848796c6ecd 100644 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java +++ b/src/main/java/net/floodlightcontroller/core/web/serializers/StatsReplySerializer.java @@ -158,7 +158,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { jGen.configure(Feature.WRITE_NUMBERS_AS_STRINGS, true); // IMHO this just looks nicer and is easier to read if everything is quoted jGen.writeStartObject(); - + if (reply.getStatType() == null) { // must be an OFFeaturesReply. getValues() was already checked for null above. serializeFeaturesReply((OFFeaturesReply) reply.getValues(), jGen); } else { @@ -213,7 +213,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { } jGen.writeEndObject(); } - + public static void serializeFeaturesReply(OFFeaturesReply fr, JsonGenerator jGen) throws IOException, JsonProcessingException { /* Common to All OF Versions */ jGen.writeStringField("capabilities", fr.getCapabilities().toString()); @@ -221,7 +221,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { jGen.writeNumberField("buffers", fr.getNBuffers()); jGen.writeNumberField("tables", fr.getNTables()); jGen.writeStringField("version", fr.getVersion().toString()); - + if (fr.getVersion().compareTo(OFVersion.OF_13) < 0) { // OF1.3+ break this out into port_config serializePortDesc(fr.getPorts(), jGen); } @@ -509,236 +509,216 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { */ public static void serializeTableFeaturesReply(List<OFTableFeaturesStatsReply> tableFeaturesReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{ - OFTableFeaturesStatsReply tableFeaturesReply = tableFeaturesReplies.get(0); - jGen.writeStringField("version", tableFeaturesReply.getVersion().toString()); //return the enum name - jGen.writeFieldName("tableFeatures"); jGen.writeStartArray(); - for(OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) { - jGen.writeStartObject(); - jGen.writeNumberField("tableId", tableFeature.getTableId().getValue()); - jGen.writeStringField("name", tableFeature.getName()); - jGen.writeNumberField("metadataMatch", tableFeature.getMetadataMatch().getValue()); - jGen.writeNumberField("metadataWrite", tableFeature.getMetadataWrite().getValue()); - jGen.writeNumberField("config", tableFeature.getConfig()); - jGen.writeNumberField("maxEntries", tableFeature.getMaxEntries()); - - jGen.writeFieldName("properties"); - jGen.writeStartArray(); - for (OFTableFeatureProp properties : tableFeature.getProperties()) { - jGen.writeStartObject(); - short type = (short)properties.getType(); - jGen.writeNumberField("tableFeaturePropType",type); - - switch (type) { - case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_VAL: - OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties; - jGen.writeFieldName("instructions"); - jGen.writeStartArray(); - for (OFInstructionId id : propInstruct.getInstructionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_MISS_VAL: - OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties; - jGen.writeFieldName("instructionsMiss"); - jGen.writeStartArray(); - for (OFInstructionId id : propInstructMiss.getInstructionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_VAL: - OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties; - jGen.writeFieldName("nextTables"); - jGen.writeStartArray(); - for (U8 id : propNxtTables.getNextTableIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_MISS_VAL: - OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties; - jGen.writeFieldName("nextTablesMiss"); - jGen.writeStartArray(); - for (U8 id : propNxtTablesMiss.getNextTableIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_VAL: - OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; - jGen.writeFieldName("writeActions"); - jGen.writeStartArray(); - for (OFActionId id : propWrAct.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_MISS_VAL: - OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties; - jGen.writeFieldName("writeActionsMiss"); - jGen.writeStartArray(); - for (OFActionId id : propWrActMiss.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_VAL: - OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties; - jGen.writeFieldName("applyActions"); - jGen.writeStartArray(); - for (OFActionId id : propAppAct.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_MISS_VAL: - OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties; - jGen.writeFieldName("applyActionsMiss"); - jGen.writeStartArray(); - for (OFActionId id : propAppActMiss.getActionIds()) { - jGen.writeStartObject(); - jGen.writeString(id.getType().toString()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.MATCH_VAL: - OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties; - jGen.writeFieldName("match"); - jGen.writeStartArray(); - for (U32 id : propMatch.getOxmIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WILDCARDS_VAL: - OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties; - jGen.writeFieldName("wildcards"); - jGen.writeStartArray(); - for (U32 id : propWildcards.getOxmIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_VAL: - OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties; - jGen.writeFieldName("writeSetfield"); - jGen.writeStartArray(); - for (U32 id : propWrSetfield.getOxmIds()) { - jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); - jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_MISS_VAL: - OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; - jGen.writeFieldName("writeSetfieldMiss"); - jGen.writeStartArray(); - for (U32 id : propWrSetfieldMiss.getOxmIds()) { + for (OFTableFeaturesStatsReply tableFeaturesReply : tableFeaturesReplies) { + + for(OFTableFeatures tableFeature : tableFeaturesReply.getEntries()) { + jGen.writeStartObject(); + jGen.writeStringField("version", tableFeature.getVersion().toString()); + jGen.writeNumberField("tableId", tableFeature.getTableId().getValue()); + jGen.writeStringField("name", tableFeature.getName()); + jGen.writeNumberField("metadataMatch", tableFeature.getMetadataMatch().getValue()); + jGen.writeNumberField("metadataWrite", tableFeature.getMetadataWrite().getValue()); + jGen.writeNumberField("config", tableFeature.getConfig()); + jGen.writeNumberField("maxEntries", tableFeature.getMaxEntries()); + + jGen.writeFieldName("properties"); + jGen.writeStartArray(); + for (OFTableFeatureProp properties : tableFeature.getProperties()) { + jGen.writeStartObject(); + + short type = (short)properties.getType(); + switch (type) { + case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_VAL: + OFTableFeaturePropInstructions propInstruct = (OFTableFeaturePropInstructions) properties; + jGen.writeFieldName("instructions"); + jGen.writeStartArray(); + for (OFInstructionId id : propInstruct.getInstructionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.INSTRUCTIONS_MISS_VAL: + OFTableFeaturePropInstructionsMiss propInstructMiss = (OFTableFeaturePropInstructionsMiss) properties; + jGen.writeFieldName("instructionsMiss"); + jGen.writeStartArray(); + for (OFInstructionId id : propInstructMiss.getInstructionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_VAL: + OFTableFeaturePropNextTables propNxtTables = (OFTableFeaturePropNextTables) properties; + jGen.writeFieldName("nextTables"); + jGen.writeStartArray(); + for (U8 id : propNxtTables.getNextTableIds()) { + jGen.writeNumber(id.getValue()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.NEXT_TABLES_MISS_VAL: + OFTableFeaturePropNextTablesMiss propNxtTablesMiss = (OFTableFeaturePropNextTablesMiss) properties; + jGen.writeFieldName("nextTablesMiss"); + jGen.writeStartArray(); + for (U8 id : propNxtTablesMiss.getNextTableIds()) { + jGen.writeNumber(id.getValue()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_VAL: + OFTableFeaturePropWriteActions propWrAct = (OFTableFeaturePropWriteActions) properties; + jGen.writeFieldName("writeActions"); + jGen.writeStartArray(); + for (OFActionId id : propWrAct.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_ACTIONS_MISS_VAL: + OFTableFeaturePropWriteActionsMiss propWrActMiss = (OFTableFeaturePropWriteActionsMiss) properties; + jGen.writeFieldName("writeActionsMiss"); + jGen.writeStartArray(); + for (OFActionId id : propWrActMiss.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_VAL: + OFTableFeaturePropApplyActions propAppAct = (OFTableFeaturePropApplyActions) properties; + jGen.writeFieldName("applyActions"); + jGen.writeStartArray(); + for (OFActionId id : propAppAct.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_ACTIONS_MISS_VAL: + OFTableFeaturePropApplyActionsMiss propAppActMiss = (OFTableFeaturePropApplyActionsMiss) properties; + jGen.writeFieldName("applyActionsMiss"); + jGen.writeStartArray(); + for (OFActionId id : propAppActMiss.getActionIds()) { + jGen.writeString(id.getType().toString()); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.MATCH_VAL: + OFTableFeaturePropMatch propMatch = (OFTableFeaturePropMatch) properties; + jGen.writeFieldName("match"); + jGen.writeStartArray(); + for (U32 id : propMatch.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WILDCARDS_VAL: + OFTableFeaturePropWildcards propWildcards = (OFTableFeaturePropWildcards) properties; + jGen.writeFieldName("wildcards"); + jGen.writeStartArray(); + for (U32 id : propWildcards.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_VAL: + OFTableFeaturePropWriteSetfield propWrSetfield = (OFTableFeaturePropWriteSetfield) properties; + jGen.writeFieldName("writeSetfield"); + jGen.writeStartArray(); + for (U32 id : propWrSetfield.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.WRITE_SETFIELD_MISS_VAL: + OFTableFeaturePropWriteSetfieldMiss propWrSetfieldMiss = (OFTableFeaturePropWriteSetfieldMiss) properties; + jGen.writeFieldName("writeSetfieldMiss"); + jGen.writeStartArray(); + for (U32 id : propWrSetfieldMiss.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_VAL: + OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties; + jGen.writeFieldName("applySetfield"); + jGen.writeStartArray(); + for (U32 id : propAppSetfield.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_MISS_VAL: + OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties; + jGen.writeFieldName("applySetfieldMiss"); + jGen.writeStartArray(); + for (U32 id : propAppSetfieldMiss.getOxmIds()) { + jGen.writeString(OXMSerializer.oxmIdToString(id)); + } + jGen.writeEndArray(); + break; + case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_VAL: + OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; + jGen.writeFieldName("experimenter"); jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); + jGen.writeNumberField("subType", propExp.getSubtype()); + jGen.writeNumberField("experimenter", propExp.getExperimenter()); + jGen.writeStringField("data", propExp.getExperimenterData().toString()); jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_VAL: - OFTableFeaturePropApplySetfield propAppSetfield = (OFTableFeaturePropApplySetfield) properties; - jGen.writeFieldName("applySetfield"); - jGen.writeStartArray(); - for (U32 id : propAppSetfield.getOxmIds()) { + break; + case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_MISS_VAL: + OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties; + jGen.writeFieldName("experimenterMiss"); jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); + jGen.writeNumberField("subType", propExpMiss.getSubtype()); + jGen.writeNumberField("experimenter", propExpMiss.getExperimenter()); + jGen.writeStringField("data", propExpMiss.getExperimenterData().toString()); jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.APPLY_SETFIELD_MISS_VAL: - OFTableFeaturePropApplySetfieldMiss propAppSetfieldMiss = (OFTableFeaturePropApplySetfieldMiss) properties; - jGen.writeFieldName("applySetfieldMiss"); - jGen.writeStartArray(); - for (U32 id : propAppSetfieldMiss.getOxmIds()) { + break; + default: + // shouldn't ever get here jGen.writeStartObject(); - jGen.writeNumber(id.getValue()); jGen.writeEndObject(); - } - jGen.writeEndArray(); - break; - case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_VAL: - OFTableFeaturePropExperimenter propExp = (OFTableFeaturePropExperimenter) properties; - jGen.writeFieldName("experimenter"); - jGen.writeStartObject(); - jGen.writeNumberField("subType", propExp.getSubtype()); - jGen.writeNumberField("experimenter", propExp.getExperimenter()); - jGen.writeStringField("subType", propExp.getExperimenterData().toString()); + break; + }//end of Switch Case jGen.writeEndObject(); - break; - case OFTableFeaturePropTypeSerializerVer13.EXPERIMENTER_MISS_VAL: - OFTableFeaturePropExperimenterMiss propExpMiss = (OFTableFeaturePropExperimenterMiss) properties; - jGen.writeFieldName("experimenterMiss"); - jGen.writeStartObject(); - jGen.writeNumberField("subType", propExpMiss.getSubtype()); - jGen.writeNumberField("experimenter", propExpMiss.getExperimenter()); - jGen.writeStringField("subType", propExpMiss.getExperimenterData().toString()); - jGen.writeEndObject(); - break; - default: - // shouldn't ever get here - break; - }//end of Switch Case + }//end of for loop - properties + jGen.writeEndArray(); jGen.writeEndObject(); - }//end of for loop - properties - jGen.writeEndObject(); - }//end of for loop - features + }//end of for loop - features + } //end of looping through REQ_MORE flagged message loop jGen.writeEndArray(); } - - public static void serializePortReply(List<OFPortStatsReply> portReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{ - OFPortStatsReply portReply = portReplies.get(0); // we will get only one PortReply and it will contains many OFPortStatsEntry ? - jGen.writeStringField("version", portReply.getVersion().toString()); //return the enum name - jGen.writeFieldName("port"); + public static void serializePortReply(List<OFPortStatsReply> portReplies, JsonGenerator jGen) throws IOException, JsonProcessingException{ + jGen.writeFieldName("port_reply"); jGen.writeStartArray(); - for(OFPortStatsEntry entry : portReply.getEntries()) { + for (OFPortStatsReply portReply : portReplies) { jGen.writeStartObject(); - jGen.writeStringField("portNumber",entry.getPortNo().toString()); - jGen.writeNumberField("receivePackets", entry.getRxPackets().getValue()); - jGen.writeNumberField("transmitPackets", entry.getTxPackets().getValue()); - jGen.writeNumberField("receiveBytes", entry.getRxBytes().getValue()); - jGen.writeNumberField("transmitBytes", entry.getTxBytes().getValue()); - jGen.writeNumberField("receiveDropped", entry.getRxDropped().getValue()); - jGen.writeNumberField("transmitDropped", entry.getTxDropped().getValue()); - jGen.writeNumberField("receiveErrors", entry.getRxErrors().getValue()); - jGen.writeNumberField("transmitErrors", entry.getTxErrors().getValue()); - jGen.writeNumberField("receiveFrameErrors", entry.getRxFrameErr().getValue()); - jGen.writeNumberField("receiveOverrunErrors", entry.getRxOverErr().getValue()); - jGen.writeNumberField("receiveCRCErrors", entry.getRxCrcErr().getValue()); - jGen.writeNumberField("collisions", entry.getCollisions().getValue()); - if (OFVersion.OF_13 == entry.getVersion()) { - jGen.writeNumberField("durationSec", entry.getDurationSec()); - jGen.writeNumberField("durationNsec", entry.getDurationNsec()); + jGen.writeStringField("version", portReply.getVersion().toString()); //return the enum name + jGen.writeFieldName("port"); + jGen.writeStartArray(); + for(OFPortStatsEntry entry : portReply.getEntries()) { + jGen.writeStartObject(); + jGen.writeStringField("portNumber",entry.getPortNo().toString()); + jGen.writeNumberField("receivePackets", entry.getRxPackets().getValue()); + jGen.writeNumberField("transmitPackets", entry.getTxPackets().getValue()); + jGen.writeNumberField("receiveBytes", entry.getRxBytes().getValue()); + jGen.writeNumberField("transmitBytes", entry.getTxBytes().getValue()); + jGen.writeNumberField("receiveDropped", entry.getRxDropped().getValue()); + jGen.writeNumberField("transmitDropped", entry.getTxDropped().getValue()); + jGen.writeNumberField("receiveErrors", entry.getRxErrors().getValue()); + jGen.writeNumberField("transmitErrors", entry.getTxErrors().getValue()); + jGen.writeNumberField("receiveFrameErrors", entry.getRxFrameErr().getValue()); + jGen.writeNumberField("receiveOverrunErrors", entry.getRxOverErr().getValue()); + jGen.writeNumberField("receiveCRCErrors", entry.getRxCrcErr().getValue()); + jGen.writeNumberField("collisions", entry.getCollisions().getValue()); + if (OFVersion.OF_13 == entry.getVersion()) { + jGen.writeNumberField("durationSec", entry.getDurationSec()); + jGen.writeNumberField("durationNsec", entry.getDurationNsec()); + } + jGen.writeEndObject(); } + jGen.writeEndArray(); jGen.writeEndObject(); } jGen.writeEndArray(); @@ -847,7 +827,7 @@ public class StatsReplySerializer extends JsonSerializer<StatsReply> { jGen.writeStringField("version", portDescReply.getVersion().toString()); //return the enum name serializePortDesc(portDescReply.getEntries(), jGen); } - + public static void serializePortDesc(List<OFPortDesc> portDescList, JsonGenerator jGen) throws IOException, JsonProcessingException { jGen.writeFieldName("portDesc"); jGen.writeStartArray(); diff --git a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java index c2a0fcdd95db1235614011b4d97713d31ba28162..9016edab905e493f4f5e64b0e041b3adcf56c53e 100644 --- a/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java +++ b/src/main/java/net/floodlightcontroller/debugevent/IDebugEventService.java @@ -32,7 +32,7 @@ public interface IDebugEventService extends IFloodlightService { * Describes the type of field obtained from reflection */ enum EventFieldType { - DPID, IPv4, MAC, STRING, OBJECT, PRIMITIVE, COLLECTION_IPV4, + DPID, IPv4, IPv6, MAC, STRING, OBJECT, PRIMITIVE, COLLECTION_IPV4, COLLECTION_ATTACHMENT_POINT, COLLECTION_OBJECT, SREF_COLLECTION_OBJECT, SREF_OBJECT } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java index 4d5ba9be6d5d5ef7f31a4316cb92d2303a62502f..072808dfbf88cc4be941a883f02aec17e66611ab 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java @@ -20,6 +20,7 @@ package net.floodlightcontroller.devicemanager; import java.util.Date; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.VlanVid; @@ -62,12 +63,19 @@ public interface IDevice { */ public IPv4Address[] getIPv4Addresses(); + /** + * Get all unique IPv6 addresses associated with the device. + * @return an array containing the unique IPv6 addresses for the device. + */ + public IPv6Address[] getIPv6Addresses(); + /** * Get all unique attachment points associated with the device. This will * not include any blocked attachment points. * @return an array containing all unique attachment points for the device */ public SwitchPort[] getAttachmentPoints(); + /** * Get all old attachment points associated with the device. this is used in host movement scenario. * @return an array containing all unique old attachment points for the device diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java index 1221e66887666765574bc543cd57a049e839f616..19b7c85bbdd1287074101b0cb83bceeff06287e5 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceListener.java @@ -55,6 +55,13 @@ public interface IDeviceListener extends IListener<String> { */ public void deviceIPV4AddrChanged(IDevice device); + /** + * Called when a network address has been added or remove from a device + * + * @param device the device that changed + */ + public void deviceIPV6AddrChanged(IDevice device); + /** * Called when a VLAN tag for the device has been added or removed * @param device the device that changed diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java index 40d729f6894149d9a1e3142c618bc606c0753a4b..54d6963fd6440ef3a389ce746afe93acd1e4dc25 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java @@ -22,8 +22,11 @@ import java.util.EnumSet; import java.util.Iterator; import java.util.Set; +import javax.annotation.Nonnull; + import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.VlanVid; import org.projectfloodlight.openflow.types.OFPort; @@ -43,7 +46,7 @@ public interface IDeviceService extends IFloodlightService { * @see IDeviceService#addIndex */ enum DeviceField { - MAC, IPV4, VLAN, SWITCH, PORT + MAC, IPv4, IPv6, VLAN, SWITCH, PORT } /** @@ -85,25 +88,26 @@ public interface IDeviceService extends IFloodlightService { * is the same lookup process that is used for packet_in processing and * device learning. Thus, findDevice() can be used to match flow entries * from switches to devices. + * * Only the key fields as defined by the {@link IEntityClassifierService} will - * be important in this search. All key fields MUST be supplied. + * be important in this search. All key fields MUST be supplied. * *{@link queryDevices()} might be more appropriate! * - * @param macAddress The MAC address - * @param vlan the VLAN. Null means no VLAN and is valid even if VLAN is a - * key field. - * @param ipv4Address the ipv4 address - * @param switchDPID the switch DPID - * @param switchPort the switch port + * @param macAddress the MAC address; use MacAddress.NONE for 'don't care' + * @param vlan the VLAN; use VlanVid.ZERO for 'untagged' + * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care' + * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care' + * @param switchDPID the switch DPID; use DatapathId.NONE for 'don't care' + * @param switchPort the switch port; use OFPort.ZERO for 'don't care' * @return an {@link IDevice} or null if no device is found. * @see IDeviceManager#setEntityClassifier(IEntityClassifierService) * @throws IllegalArgumentException if not all key fields of the * current {@link IEntityClassifierService} are specified. */ - public IDevice findDevice(MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, DatapathId switchDPID, - OFPort switchPort) + public IDevice findDevice(@Nonnull MacAddress macAddress, VlanVid vlan, + @Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address, + @Nonnull DatapathId switchDPID, @Nonnull OFPort switchPort) throws IllegalArgumentException; /** @@ -118,18 +122,18 @@ public interface IDeviceService extends IFloodlightService { * be important in this search. All key fields MUST be supplied. * * @param entityClass The entity class in which to perform the lookup. - * @param macAddress The MAC address for the destination - * @param vlan the VLAN if available - * @param ipv4Address The IP address if available. + * @param macAddress the MAC address; use MacAddress.NONE for 'don't care' + * @param vlan the VLAN; use VlanVid.ZERO for 'untagged' + * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care' + * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care' * @return an {@link IDevice} or null if no device is found. - * @see IDeviceService#findDevice(long, Short, Integer, Long, - * Integer) + * @see IDeviceService#findDevice(MacAddress, VlanVid, IPv4Address, IPv6Address, DatapathId, OFPort) * @throws IllegalArgumentException if not all key fields of the * source's {@link IEntityClass} are specified. */ - public IDevice findClassDevice(IEntityClass entityClass, - MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address) + public IDevice findClassDevice(@Nonnull IEntityClass entityClass, + @Nonnull MacAddress macAddress, VlanVid vlan, + @Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address) throws IllegalArgumentException; /** @@ -159,20 +163,22 @@ public interface IDeviceService extends IFloodlightService { * the query, then it will be performed efficiently using the index. * Otherwise, there will be a full scan of the device list. * - * @param macAddress The MAC address - * @param vlan the VLAN - * @param ipv4Address the ipv4 address - * @param switchDPID the switch DPID - * @param switchPort the switch port + * @param macAddress the MAC address; use MacAddress.NONE for 'don't care' + * @param vlan the VLAN; use null for 'don't care'; use VlanVid.ZERO for 'untagged' + * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care' + * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care' + * @param switchDPID the switch DPID; use DatapathId.NONE for 'don't care' + * @param switchPort the switch port; use OFPort.ZERO for 'don't care' * @return an iterator over a set of devices matching the query - * @see IDeviceService#queryClassDevices(IEntityClass, Long, - * Short, Integer, Long, Integer) + * @see IDeviceService#queryClassDevices(IEntityClass, MacAddress, + * VlanVid, IPv4Address, IPv6Address, DatapathId, OFPort) */ - public Iterator<? extends IDevice> queryDevices(MacAddress macAddress, + public Iterator<? extends IDevice> queryDevices(@Nonnull MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort); + @Nonnull IPv4Address ipv4Address, + @Nonnull IPv6Address ipv6Address, + @Nonnull DatapathId switchDPID, + @Nonnull OFPort switchPort); /** * Find devices that match the provided query. Only the index for @@ -183,19 +189,21 @@ public interface IDeviceService extends IFloodlightService { * of the device list. * * @param entityClass The entity class in which to perform the query - * @param macAddress The MAC address - * @param vlan the VLAN - * @param ipv4Address the ipv4 address - * @param switchDPID the switch DPID - * @param switchPort the switch port - * @return an iterator over a set of devices matching the query - * @see IDeviceService#queryClassDevices(Long, - * Short, Integer, Long, Integer) + * @param macAddress the MAC address; use MacAddress.NONE for 'don't care' + * @param vlan the VLAN; use null for 'don't care'; use VlanVid.ZERO for 'untagged' + * @param ipv4Address the ipv4 address; use IPv4Address.NONE for 'don't care' + * @param ipv6Address the ipv6 address; use IPv6Address.NONE for 'don't care' + * @param switchDPID the switch DPID; use DatapathId.NONE for 'don't care' + * @param switchPort the switch port; use OFPort.ZERO for 'don't care' + * @return an iterator over a set of devices matching the query + * @see IDeviceService#queryClassDevices(MacAddress, + * VlanVid, IPv4Address, IPv6Address, DatapathId, OFPort) */ public Iterator<? extends IDevice> queryClassDevices(IEntityClass entityClass, MacAddress macAddress, VlanVid vlan, IPv4Address ipv4Address, + IPv6Address ipv6Address, DatapathId switchDPID, OFPort switchPort); diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java index 1845ffe8c47a7fbc65cfd50c247aee24e246b3e6..79777f8133e5d82cbf77f720da4037293fadf756 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/AttachmentPoint.java @@ -125,4 +125,4 @@ public class AttachmentPoint { + ", activeSince=" + activeSince + ", lastSeen=" + lastSeen.toString() + "]"; } -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java index ea55f8cac2bba0f33746375ff96b40a8ad6825f4..b8d83272254797d19d311985eaec5a80862fbf87 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DefaultEntityClassifier.java @@ -125,6 +125,5 @@ public class DefaultEntityClassifier implements @Override public void addListener(IEntityClassListener listener) { // no-op - } -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index d751e6fead99131dc9682b6c3fd7268f550bb664..9f028cf749b17434a422b5e2e6a9cc4f46800f6a 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -24,15 +24,18 @@ import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.TreeSet; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -49,690 +52,804 @@ import net.floodlightcontroller.topology.ITopologyService; /** * Concrete implementation of {@link IDevice} + * * @author readams */ -@JsonSerialize(using=DeviceSerializer.class) +@JsonSerialize(using = DeviceSerializer.class) public class Device implements IDevice { - protected static Logger log = LoggerFactory.getLogger(Device.class); - - private final Long deviceKey; - protected final DeviceManagerImpl deviceManager; - - protected final Entity[] entities; - private final IEntityClass entityClass; - - protected final String macAddressString; - // the vlan Ids from the entities of this device - protected final VlanVid[] vlanIds; - protected volatile String dhcpClientName; - - /** - * These are the old attachment points for the device that were - * valid no more than INACTIVITY_TIME ago. - */ - protected volatile List<AttachmentPoint> oldAPs; - /** - * The current attachment points for the device. - */ - protected volatile List<AttachmentPoint> attachmentPoints; - - // ************ - // Constructors - // ************ - - /** - * Create a device from an entities - * @param deviceManager the device manager for this device - * @param deviceKey the unique identifier for this device object - * @param entity the initial entity for the device - * @param entityClass the entity classes associated with the entity - */ - public Device(DeviceManagerImpl deviceManager, Long deviceKey, - Entity entity, IEntityClass entityClass) { - this.deviceManager = deviceManager; - this.deviceKey = deviceKey; - this.entities = new Entity[] {entity}; - this.macAddressString = entity.getMacAddress().toString(); - this.entityClass = entityClass; - Arrays.sort(this.entities); - - this.dhcpClientName = null; - this.oldAPs = null; - this.attachmentPoints = null; - - if (entity.getSwitchDPID() != null && entity.getSwitchPort() != null) { - DatapathId sw = entity.getSwitchDPID(); - OFPort port = entity.getSwitchPort(); - - if (deviceManager.isValidAttachmentPoint(sw, port)) { - AttachmentPoint ap; - ap = new AttachmentPoint(sw, port, entity.getLastSeenTimestamp()); - this.attachmentPoints = new ArrayList<AttachmentPoint>(); - this.attachmentPoints.add(ap); - } - } - vlanIds = computeVlandIds(); - } - - /** - * Create a device from a set of entities - * @param deviceManager the device manager for this device - * @param deviceKey the unique identifier for this device object - * @param entities the initial entities for the device - * @param entityClass the entity class associated with the entities - */ - public Device(DeviceManagerImpl deviceManager, - Long deviceKey, - String dhcpClientName, - Collection<AttachmentPoint> oldAPs, - Collection<AttachmentPoint> attachmentPoints, - Collection<Entity> entities, - IEntityClass entityClass) { - this.deviceManager = deviceManager; - this.deviceKey = deviceKey; - this.dhcpClientName = dhcpClientName; - this.entities = entities.toArray(new Entity[entities.size()]); - this.oldAPs = null; - this.attachmentPoints = null; - if (oldAPs != null) { - this.oldAPs = new ArrayList<AttachmentPoint>(oldAPs); - } - if (attachmentPoints != null) { - this.attachmentPoints = new ArrayList<AttachmentPoint>(attachmentPoints); - } - this.macAddressString = this.entities[0].getMacAddress().toString(); - this.entityClass = entityClass; - Arrays.sort(this.entities); - vlanIds = computeVlandIds(); - } - - /** - * Construct a new device consisting of the entities from the old device - * plus an additional entity. - * The caller needs to ensure that the additional entity is not already - * present in the array - * @param device the old device object - * @param newEntity the entity to add. newEntity must be have the same - * entity class as device - * @param if positive indicates the index in the entities array were the - * new entity should be inserted. If negative we will compute the - * correct insertion point - */ - public Device(Device device, Entity newEntity, int insertionpoint) { - this.deviceManager = device.deviceManager; - this.deviceKey = device.deviceKey; - this.dhcpClientName = device.dhcpClientName; - - this.entities = new Entity[device.entities.length + 1]; - if (insertionpoint < 0) { - insertionpoint = -(Arrays.binarySearch(device.entities, newEntity) + 1); - } - if (insertionpoint > 0) { - // insertion point is not the beginning: - // copy up to insertion point - System.arraycopy(device.entities, 0, this.entities, 0, insertionpoint); - } - if (insertionpoint < device.entities.length) { - // insertion point is not the end - // copy from insertion point - System.arraycopy(device.entities, insertionpoint, - this.entities, insertionpoint + 1, - device.entities.length - insertionpoint); - } - this.entities[insertionpoint] = newEntity; - /* - this.entities = Arrays.<Entity>copyOf(device.entities, - device.entities.length + 1); - this.entities[this.entities.length - 1] = newEntity; - Arrays.sort(this.entities); - */ - this.oldAPs = null; - if (device.oldAPs != null) { - this.oldAPs = new ArrayList<AttachmentPoint>(device.oldAPs); - } - this.attachmentPoints = null; - if (device.attachmentPoints != null) { - this.attachmentPoints = new ArrayList<AttachmentPoint>(device.attachmentPoints); - } - - this.macAddressString = this.entities[0].getMacAddress().toString(); - - this.entityClass = device.entityClass; - vlanIds = computeVlandIds(); - } - - private VlanVid[] computeVlandIds() { - if (entities.length == 1) { - if (entities[0].getVlan() != null) { - return new VlanVid[]{ entities[0].getVlan() }; - } else { - return new VlanVid[] { VlanVid.ofVlan(-1) }; - } - } - - TreeSet<VlanVid> vals = new TreeSet<VlanVid>(); - for (Entity e : entities) { - if (e.getVlan() == null) - vals.add(VlanVid.ofVlan(-1)); - else - vals.add(e.getVlan()); - } - return vals.toArray(new VlanVid[vals.size()]); - } - - /** - * Given a list of attachment points (apList), the procedure would return - * a map of attachment points for each L2 domain. L2 domain id is the key. - * @param apList - * @return - */ - private Map<DatapathId, AttachmentPoint> getAPMap(List<AttachmentPoint> apList) { - - if (apList == null) return null; - ITopologyService topology = deviceManager.topology; - - // Get the old attachment points and sort them. - List<AttachmentPoint>oldAP = new ArrayList<AttachmentPoint>(); - if (apList != null) oldAP.addAll(apList); - - // Remove invalid attachment points before sorting. - List<AttachmentPoint>tempAP = - new ArrayList<AttachmentPoint>(); - for(AttachmentPoint ap: oldAP) { - if (deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort())) { - tempAP.add(ap); - } - } - oldAP = tempAP; - - Collections.sort(oldAP, deviceManager.apComparator); - - // Map of attachment point by L2 domain Id. - Map<DatapathId, AttachmentPoint> apMap = new HashMap<DatapathId, AttachmentPoint>(); - - for(int i=0; i<oldAP.size(); ++i) { - AttachmentPoint ap = oldAP.get(i); - // if this is not a valid attachment point, continue - if (!deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort())) - continue; - - DatapathId id = topology.getL2DomainId(ap.getSw()); - apMap.put(id, ap); - } - - if (apMap.isEmpty()) return null; - return apMap; - } - - /** - * Remove all attachment points that are older than INACTIVITY_INTERVAL - * from the list. - * @param apList - * @return - */ - private boolean removeExpiredAttachmentPoints(List<AttachmentPoint>apList) { - - List<AttachmentPoint> expiredAPs = new ArrayList<AttachmentPoint>(); - - if (apList == null) return false; - - for(AttachmentPoint ap: apList) { - if (ap.getLastSeen().getTime() + AttachmentPoint.INACTIVITY_INTERVAL < System.currentTimeMillis()) { - expiredAPs.add(ap); - } - } - if (expiredAPs.size() > 0) { - apList.removeAll(expiredAPs); - return true; - } else return false; - } - - /** - * Get a list of duplicate attachment points, given a list of old attachment - * points and one attachment point per L2 domain. Given a true attachment - * point in the L2 domain, say trueAP, another attachment point in the - * same L2 domain, say ap, is duplicate if: - * 1. ap is inconsistent with trueAP, and - * 2. active time of ap is after that of trueAP; and - * 3. last seen time of ap is within the last INACTIVITY_INTERVAL - * @param oldAPList - * @param apMap - * @return - */ - List<AttachmentPoint> getDuplicateAttachmentPoints(List<AttachmentPoint>oldAPList, Map<DatapathId, AttachmentPoint>apMap) { - ITopologyService topology = deviceManager.topology; - List<AttachmentPoint> dupAPs = new ArrayList<AttachmentPoint>(); - long timeThreshold = System.currentTimeMillis() - AttachmentPoint.INACTIVITY_INTERVAL; - - if (oldAPList == null || apMap == null) - return dupAPs; - - for(AttachmentPoint ap : oldAPList) { - DatapathId id = topology.getL2DomainId(ap.getSw()); - AttachmentPoint trueAP = apMap.get(id); - - if (trueAP == null) continue; - boolean c = (topology.isConsistent(trueAP.getSw(), trueAP.getPort(), - ap.getSw(), ap.getPort())); - boolean active = (ap.getActiveSince().after(trueAP.getActiveSince())); - boolean last = ap.getLastSeen().getTime() > timeThreshold; - if (!c && active && last) { - dupAPs.add(ap); - } - } - - return dupAPs; - } - - /** - * Update the known attachment points. This method is called whenever - * topology changes. The method returns true if there's any change to - * the list of attachment points -- which indicates a possible device - * move. - * @return - */ - protected boolean updateAttachmentPoint() { - boolean moved = false; - this.oldAPs = attachmentPoints; - if (attachmentPoints == null || attachmentPoints.isEmpty()) - return false; - - List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>(); - if (attachmentPoints != null) apList.addAll(attachmentPoints); - Map<DatapathId, AttachmentPoint> newMap = getAPMap(apList); - if (newMap == null || newMap.size() != apList.size()) { - moved = true; - } - - // Prepare the new attachment point list. - if (moved) { - log.info("updateAttachmentPoint: ap {} newmap {} ", attachmentPoints, newMap); - List<AttachmentPoint> newAPList = new ArrayList<AttachmentPoint>(); - if (newMap != null) newAPList.addAll(newMap.values()); - this.attachmentPoints = newAPList; - } - - // Set the oldAPs to null. - return moved; - } - - /** - * Update the list of attachment points given that a new packet-in - * was seen from (sw, port) at time (lastSeen). The return value is true - * if there was any change to the list of attachment points for the device - * -- which indicates a device move. - * @param sw - * @param port - * @param lastSeen - * @return - */ - protected boolean updateAttachmentPoint(DatapathId sw, OFPort port, Date lastSeen){ - ITopologyService topology = deviceManager.topology; - List<AttachmentPoint> oldAPList; - List<AttachmentPoint> apList; - boolean oldAPFlag = false; - - if (!deviceManager.isValidAttachmentPoint(sw, port)) return false; - AttachmentPoint newAP = new AttachmentPoint(sw, port, lastSeen); - //Copy the oldAP and ap list. - apList = new ArrayList<AttachmentPoint>(); - if (attachmentPoints != null) apList.addAll(attachmentPoints); - oldAPList = new ArrayList<AttachmentPoint>(); - if (oldAPs != null) oldAPList.addAll(oldAPs); - - // if the sw, port is in old AP, remove it from there - // and update the lastSeen in that object. - if (oldAPList.contains(newAP)) { - int index = oldAPList.indexOf(newAP); - newAP = oldAPList.remove(index); - newAP.setLastSeen(lastSeen); - this.oldAPs = oldAPList; - oldAPFlag = true; - } - - // newAP now contains the new attachment point. - - // Get the APMap is null or empty. - Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList); - if (apMap == null || apMap.isEmpty()) { - apList.add(newAP); - attachmentPoints = apList; - // there are no old attachment points - since the device exists, this - // may be because the host really moved (so the old AP port went down); - // or it may be because the switch restarted (so old APs were nullified). - // For now we will treat both cases as host moved. - return true; - } - - DatapathId id = topology.getL2DomainId(sw); - AttachmentPoint oldAP = apMap.get(id); - - if (oldAP == null) { // No attachment on this L2 domain. - apList = new ArrayList<AttachmentPoint>(); - apList.addAll(apMap.values()); - apList.add(newAP); - this.attachmentPoints = apList; - return true; // new AP found on an L2 island. - } - - // There is already a known attachment point on the same L2 island. - // we need to compare oldAP and newAP. - if (oldAP.equals(newAP)) { - // nothing to do here. just the last seen has to be changed. - if (newAP.lastSeen.after(oldAP.lastSeen)) { - oldAP.setLastSeen(newAP.lastSeen); - } - this.attachmentPoints = - new ArrayList<AttachmentPoint>(apMap.values()); - return false; // nothing to do here. - } - - int x = deviceManager.apComparator.compare(oldAP, newAP); - if (x < 0) { - // newAP replaces oldAP. - apMap.put(id, newAP); - this.attachmentPoints = - new ArrayList<AttachmentPoint>(apMap.values()); - - oldAPList = new ArrayList<AttachmentPoint>(); - if (oldAPs != null) oldAPList.addAll(oldAPs); - oldAPList.add(oldAP); - this.oldAPs = oldAPList; - if (!topology.isInSameBroadcastDomain(oldAP.getSw(), oldAP.getPort(), - newAP.getSw(), newAP.getPort())) - return true; // attachment point changed. - } else if (oldAPFlag) { - // retain oldAP as is. Put the newAP in oldAPs for flagging - // possible duplicates. - oldAPList = new ArrayList<AttachmentPoint>(); - if (oldAPs != null) oldAPList.addAll(oldAPs); - // Add ot oldAPList only if it was picked up from the oldAPList - oldAPList.add(newAP); - this.oldAPs = oldAPList; - } - return false; - } - - /** - * Delete (sw,port) from the list of list of attachment points - * and oldAPs. - * @param sw - * @param port - * @return - */ - public boolean deleteAttachmentPoint(DatapathId sw, OFPort port) { - AttachmentPoint ap = new AttachmentPoint(sw, port, new Date(0)); - - if (this.oldAPs != null) { - ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>(); - apList.addAll(this.oldAPs); - int index = apList.indexOf(ap); - if (index > 0) { - apList.remove(index); - this.oldAPs = apList; - } - } - - if (this.attachmentPoints != null) { - ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>(); - apList.addAll(this.attachmentPoints); - int index = apList.indexOf(ap); - if (index > 0) { - apList.remove(index); - this.attachmentPoints = apList; - return true; - } - } - return false; - } - - public boolean deleteAttachmentPoint(DatapathId sw) { - boolean deletedFlag; - ArrayList<AttachmentPoint> apList; - ArrayList<AttachmentPoint> modifiedList; - - // Delete the APs on switch sw in oldAPs. - deletedFlag = false; - apList = new ArrayList<AttachmentPoint>(); - if (this.oldAPs != null) - apList.addAll(this.oldAPs); - modifiedList = new ArrayList<AttachmentPoint>(); - - for(AttachmentPoint ap: apList) { - if (ap.getSw().equals(sw)) { - deletedFlag = true; - } else { - modifiedList.add(ap); - } - } - - if (deletedFlag) { - this.oldAPs = modifiedList; - } - - // Delete the APs on switch sw in attachmentPoints. - deletedFlag = false; - apList = new ArrayList<AttachmentPoint>(); - if (this.attachmentPoints != null) - apList.addAll(this.attachmentPoints); - modifiedList = new ArrayList<AttachmentPoint>(); - - for(AttachmentPoint ap: apList) { - if (ap.getSw().equals(sw)) { - deletedFlag = true; - } else { - modifiedList.add(ap); - } - } - - if (deletedFlag) { - this.attachmentPoints = modifiedList; - return true; - } - - return false; - } - - // ******* - // IDevice - // ******* - - @Override - public SwitchPort[] getOldAP() { - List<SwitchPort> sp = new ArrayList<SwitchPort>(); - SwitchPort [] returnSwitchPorts = new SwitchPort[] {}; - if (oldAPs == null) return returnSwitchPorts; - if (oldAPs.isEmpty()) return returnSwitchPorts; - - // copy ap list. - List<AttachmentPoint> oldAPList; - oldAPList = new ArrayList<AttachmentPoint>(); - - if (oldAPs != null) oldAPList.addAll(oldAPs); - removeExpiredAttachmentPoints(oldAPList); - - if (oldAPList != null) { - for(AttachmentPoint ap: oldAPList) { - SwitchPort swport = new SwitchPort(ap.getSw(), - ap.getPort()); - sp.add(swport); - } - } - return sp.toArray(new SwitchPort[sp.size()]); - } - - @Override - public SwitchPort[] getAttachmentPoints() { - return getAttachmentPoints(false); - } - - @Override - public SwitchPort[] getAttachmentPoints(boolean includeError) { - List<SwitchPort> sp = new ArrayList<SwitchPort>(); - SwitchPort [] returnSwitchPorts = new SwitchPort[] {}; - if (attachmentPoints == null) return returnSwitchPorts; - if (attachmentPoints.isEmpty()) return returnSwitchPorts; - - // copy ap list. - List<AttachmentPoint> apList = attachmentPoints; - - if (apList != null) { - for(AttachmentPoint ap: apList) { - SwitchPort swport = new SwitchPort(ap.getSw(), - ap.getPort()); - sp.add(swport); - } - } - - if (!includeError) - return sp.toArray(new SwitchPort[sp.size()]); - - List<AttachmentPoint> oldAPList; - oldAPList = new ArrayList<AttachmentPoint>(); - - if (oldAPs != null) oldAPList.addAll(oldAPs); - - if (removeExpiredAttachmentPoints(oldAPList)) - this.oldAPs = oldAPList; - - List<AttachmentPoint> dupList; - // get AP map. - Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList); - dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap); - if (dupList != null) { - for(AttachmentPoint ap: dupList) { - SwitchPort swport = new SwitchPort(ap.getSw(), - ap.getPort(), - ErrorStatus.DUPLICATE_DEVICE); - sp.add(swport); - } - } - return sp.toArray(new SwitchPort[sp.size()]); - } - - @Override - public Long getDeviceKey() { - return deviceKey; - } - - @Override - public MacAddress getMACAddress() { - // we assume only one MAC per device for now. - return entities[0].getMacAddress(); - } - - @Override - public String getMACAddressString() { - return macAddressString; - } - - @Override - public VlanVid[] getVlanId() { - return Arrays.copyOf(vlanIds, vlanIds.length); - } - - static final EnumSet<DeviceField> ipv4Fields = EnumSet.of(DeviceField.IPV4); - - @Override - public IPv4Address[] getIPv4Addresses() { - // XXX - TODO we can cache this result. Let's find out if this - // is really a performance bottleneck first though. - - TreeSet<IPv4Address> vals = new TreeSet<IPv4Address>(); - for (Entity e : entities) { - if (e.getIpv4Address() == null) continue; - - // We have an IP address only if among the devices within the class - // we have the most recent entity with that IP. - boolean validIP = true; - Iterator<Device> devices = - deviceManager.queryClassByEntity(entityClass, ipv4Fields, e); - while (devices.hasNext()) { - Device d = devices.next(); - if (deviceKey.equals(d.getDeviceKey())) - continue; - for (Entity se : d.entities) { - if (se.getIpv4Address() != null && - se.getIpv4Address().equals(e.getIpv4Address()) && - se.getLastSeenTimestamp() != null && - 0 < se.getLastSeenTimestamp(). - compareTo(e.getLastSeenTimestamp())) { - validIP = false; - break; - } - } - if (!validIP) - break; - } - - if (validIP) - vals.add(e.getIpv4Address()); - } - - return vals.toArray(new IPv4Address[vals.size()]); - } - - @Override - public VlanVid[] getSwitchPortVlanIds(SwitchPort swp) { - TreeSet<VlanVid> vals = new TreeSet<VlanVid>(); - for (Entity e : entities) { - if (e.switchDPID.equals(swp.getSwitchDPID()) - && e.switchPort.equals(swp.getPort())) { - if (e.getVlan() == null) - vals.add(VlanVid.ofVlan(-1)); //TODO Update all -1 VLANs (untagged) to the new VlanVid.ZERO - else - vals.add(e.getVlan()); - } - } - return vals.toArray(new VlanVid[vals.size()]); - } - - @Override - public Date getLastSeen() { - Date d = null; - for (int i = 0; i < entities.length; i++) { - if (d == null || - entities[i].getLastSeenTimestamp().compareTo(d) > 0) - d = entities[i].getLastSeenTimestamp(); - } - return d; - } - - // *************** - // Getters/Setters - // *************** - - @Override - public IEntityClass getEntityClass() { - return entityClass; - } - - public Entity[] getEntities() { - return entities; - } - - public String getDHCPClientName() { - return dhcpClientName; - } - - // *************** - // Utility Methods - // *************** - - /** - * Check whether the device contains the specified entity - * @param entity the entity to search for - * @return the index of the entity, or <0 if not found - */ - protected int entityIndex(Entity entity) { - return Arrays.binarySearch(entities, entity); - } - - // ****** - // Object - // ****** - - @Override + protected static Logger log = LoggerFactory.getLogger(Device.class); + + private final Long deviceKey; + protected final DeviceManagerImpl deviceManager; + + protected final Entity[] entities; + private final IEntityClass entityClass; + + protected final String macAddressString; + // the vlan Ids from the entities of this device + protected final VlanVid[] vlanIds; + protected volatile String dhcpClientName; + + /** + * These are the old attachment points for the device that were valid no + * more than INACTIVITY_TIME ago. + */ + protected volatile List<AttachmentPoint> oldAPs; + /** + * The current attachment points for the device. + */ + protected volatile List<AttachmentPoint> attachmentPoints; + + // ************ + // Constructors + // ************ + + /** + * Create a device from an entities + * + * @param deviceManager + * the device manager for this device + * @param deviceKey + * the unique identifier for this device object + * @param entity + * the initial entity for the device + * @param entityClass + * the entity classes associated with the entity + */ + public Device(DeviceManagerImpl deviceManager, Long deviceKey, + Entity entity, IEntityClass entityClass) { + this.deviceManager = deviceManager; + this.deviceKey = deviceKey; + this.entities = new Entity[] { entity }; + this.macAddressString = entity.getMacAddress().toString(); + this.entityClass = entityClass; + Arrays.sort(this.entities); + + this.dhcpClientName = null; + this.oldAPs = null; + this.attachmentPoints = null; + + if (!entity.getSwitchDPID().equals(DatapathId.NONE) + && !entity.getSwitchPort().equals(OFPort.ZERO)) { + DatapathId sw = entity.getSwitchDPID(); + OFPort port = entity.getSwitchPort(); + + if (deviceManager.isValidAttachmentPoint(sw, port)) { + AttachmentPoint ap; + ap = new AttachmentPoint(sw, port, + entity.getLastSeenTimestamp()); + this.attachmentPoints = new ArrayList<AttachmentPoint>(); + this.attachmentPoints.add(ap); + } + } + vlanIds = computeVlandIds(); + } + + /** + * Create a device from a set of entities + * + * @param deviceManager + * the device manager for this device + * @param deviceKey + * the unique identifier for this device object + * @param entities + * the initial entities for the device + * @param entityClass + * the entity class associated with the entities + */ + public Device(DeviceManagerImpl deviceManager, Long deviceKey, + String dhcpClientName, Collection<AttachmentPoint> oldAPs, + Collection<AttachmentPoint> attachmentPoints, + Collection<Entity> entities, IEntityClass entityClass) { + this.deviceManager = deviceManager; + this.deviceKey = deviceKey; + this.dhcpClientName = dhcpClientName; + this.entities = entities.toArray(new Entity[entities.size()]); + this.oldAPs = null; + this.attachmentPoints = null; + if (oldAPs != null) { + this.oldAPs = new ArrayList<AttachmentPoint>(oldAPs); + } + if (attachmentPoints != null) { + this.attachmentPoints = new ArrayList<AttachmentPoint>( + attachmentPoints); + } + this.macAddressString = this.entities[0].getMacAddress().toString(); + this.entityClass = entityClass; + Arrays.sort(this.entities); + vlanIds = computeVlandIds(); + } + + /** + * Construct a new device consisting of the entities from the old device + * plus an additional entity. The caller needs to ensure that the additional + * entity is not already present in the array + * + * @param device + * the old device object + * @param newEntity + * the entity to add. newEntity must be have the same entity + * class as device + * @param if positive indicates the index in the entities array were the new + * entity should be inserted. If negative we will compute the correct + * insertion point + */ + public Device(Device device, Entity newEntity, int insertionpoint) { + this.deviceManager = device.deviceManager; + this.deviceKey = device.deviceKey; + this.dhcpClientName = device.dhcpClientName; + + if (insertionpoint < 0) { + insertionpoint = -(Arrays.binarySearch(device.entities, newEntity) + 1); + } + + /* + * If insertion point is still negative, then the entity exists already. + * Replace it with the new entity. + */ + if (insertionpoint < 0) { + log.warn("Performing a replacement upon new entity add in Device. Should the entity have been removed first?"); + this.entities = new Entity[device.entities.length]; + int replacementpoint = -insertionpoint - 1; /* get the original binarySearch return (the positive 0-relative position) */ + System.arraycopy(device.entities, 0, this.entities, 0, replacementpoint); /* 0 to replacementpoint-1 (or replacementpoint replacements) */ + this.entities[replacementpoint] = newEntity; + System.arraycopy(device.entities, replacementpoint + 1, this.entities, replacementpoint + 1, device.entities.length - (replacementpoint + 1)); + } else { + this.entities = new Entity[device.entities.length + 1]; + if (insertionpoint > 0) { + // insertion point is not the beginning: + // copy up to insertion point + System.arraycopy(device.entities, 0, this.entities, 0, + insertionpoint); + } + if (insertionpoint < device.entities.length) { + // insertion point is not the end + // copy from insertion point + System.arraycopy(device.entities, insertionpoint, this.entities, + insertionpoint + 1, device.entities.length - insertionpoint); + } + this.entities[insertionpoint] = newEntity; + } + /* + * this.entities = Arrays.<Entity>copyOf(device.entities, + * device.entities.length + 1); this.entities[this.entities.length - 1] + * = newEntity; Arrays.sort(this.entities); + */ + this.oldAPs = null; + if (device.oldAPs != null) { + this.oldAPs = new ArrayList<AttachmentPoint>(device.oldAPs); + } + this.attachmentPoints = null; + if (device.attachmentPoints != null) { + this.attachmentPoints = new ArrayList<AttachmentPoint>( + device.attachmentPoints); + } + + this.macAddressString = this.entities[0].getMacAddress().toString(); + + this.entityClass = device.entityClass; + vlanIds = computeVlandIds(); + } + + private VlanVid[] computeVlandIds() { + if (entities.length == 1) { + return new VlanVid[] { entities[0].getVlan() }; + } + + TreeSet<VlanVid> vals = new TreeSet<VlanVid>(); + for (Entity e : entities) { + vals.add(e.getVlan()); + } + return vals.toArray(new VlanVid[vals.size()]); + } + + /** + * Given a list of attachment points (apList), the procedure would return a + * map of attachment points for each L2 domain. L2 domain id is the key. + * + * @param apList + * @return + */ + private Map<DatapathId, AttachmentPoint> getAPMap( + List<AttachmentPoint> apList) { + + if (apList == null) + return null; + + // Get the old attachment points and sort them. + List<AttachmentPoint> oldAP = new ArrayList<AttachmentPoint>(); + if (apList != null) { + oldAP.addAll(apList); + } + + // Remove invalid attachment points before sorting. + List<AttachmentPoint> tempAP = new ArrayList<AttachmentPoint>(); + for (AttachmentPoint ap : oldAP) { + if (deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort())) { + tempAP.add(ap); + } + } + oldAP = tempAP; + + Collections.sort(oldAP, deviceManager.apComparator); + + // Map of attachment point by L2 domain Id. + Map<DatapathId, AttachmentPoint> apMap = new HashMap<DatapathId, AttachmentPoint>(); + + for (int i = 0; i < oldAP.size(); ++i) { + AttachmentPoint ap = oldAP.get(i); + // if this is not a valid attachment point, continue + if (!deviceManager.isValidAttachmentPoint(ap.getSw(), ap.getPort())) + continue; + + DatapathId id = deviceManager.topology.getL2DomainId(ap.getSw()); + apMap.put(id, ap); + } + + if (apMap.isEmpty()) + return null; + return apMap; + } + + /** + * Remove all attachment points that are older than INACTIVITY_INTERVAL from + * the list. + * + * @param apList + * @return + */ + private boolean removeExpiredAttachmentPoints(List<AttachmentPoint> apList) { + + List<AttachmentPoint> expiredAPs = new ArrayList<AttachmentPoint>(); + + if (apList == null) + return false; + + for (AttachmentPoint ap : apList) { + if (ap.getLastSeen().getTime() + + AttachmentPoint.INACTIVITY_INTERVAL < System + .currentTimeMillis()) { + expiredAPs.add(ap); + } + } + if (expiredAPs.size() > 0) { + apList.removeAll(expiredAPs); + return true; + } else + return false; + } + + /** + * Get a list of duplicate attachment points, given a list of old attachment + * points and one attachment point per L2 domain. Given a true attachment + * point in the L2 domain, say trueAP, another attachment point in the same + * L2 domain, say ap, is duplicate if: 1. ap is inconsistent with trueAP, + * and 2. active time of ap is after that of trueAP; and 3. last seen time + * of ap is within the last INACTIVITY_INTERVAL + * + * @param oldAPList + * @param apMap + * @return + */ + List<AttachmentPoint> getDuplicateAttachmentPoints( + List<AttachmentPoint> oldAPList, + Map<DatapathId, AttachmentPoint> apMap) { + ITopologyService topology = deviceManager.topology; + List<AttachmentPoint> dupAPs = new ArrayList<AttachmentPoint>(); + long timeThreshold = System.currentTimeMillis() - AttachmentPoint.INACTIVITY_INTERVAL; + + if (oldAPList == null || apMap == null) { + return dupAPs; + } + + Set<DatapathId> visitedIslands = new HashSet<DatapathId>(); + + for (AttachmentPoint ap : oldAPList) { + DatapathId id = topology.getL2DomainId(ap.getSw()); + AttachmentPoint trueAP = apMap.get(id); + + if (trueAP == null) { + continue; + } + boolean c = (topology.isConsistent(trueAP.getSw(), + trueAP.getPort(), ap.getSw(), ap.getPort())); + boolean active = trueAP.getActiveSince().after(ap.getActiveSince()) + && ap.getLastSeen().after(trueAP.getLastSeen()); + boolean last = ap.getLastSeen().getTime() > timeThreshold; + if (!c && active && last) { + visitedIslands.add(id); + } + } + + /* + * Now that we've found the islands with a duplicate, + * go back and for every AP on those islands that + * has not expired, add them as duplicates to the list. + */ + for (AttachmentPoint ap : oldAPList) { + DatapathId id = topology.getL2DomainId(ap.getSw()); + if (visitedIslands.contains(id)) { + if (ap.getLastSeen().getTime() > timeThreshold) { + dupAPs.add(ap); + } + } + } + + return dupAPs; + } + + /** + * Update the known attachment points. This method is called whenever + * topology changes. The method returns true if there's any change to the + * list of attachment points -- which indicates a possible device move. + * + * @return + */ + protected boolean updateAttachmentPoint() { + boolean moved = false; + this.oldAPs = attachmentPoints; + if (attachmentPoints == null || attachmentPoints.isEmpty()) { + return false; + } + + List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>(); + if (attachmentPoints != null) { + apList.addAll(attachmentPoints); + } + Map<DatapathId, AttachmentPoint> newMap = getAPMap(apList); + if (newMap == null || newMap.size() != apList.size()) { + moved = true; + } + + // Prepare the new attachment point list + if (moved) { + log.info("updateAttachmentPoint: ap {} newmap {} ", + attachmentPoints, newMap); + List<AttachmentPoint> newAPList = new ArrayList<AttachmentPoint>(); + if (newMap != null) { + newAPList.addAll(newMap.values()); + } + this.attachmentPoints = newAPList; + } + + // Set the oldAPs to null. + return moved; + } + + /** + * Update the list of attachment points given that a new packet-in was seen + * from (sw, port) at time (lastSeen). The return value is true if there was + * any change to the list of attachment points for the device -- which + * indicates a device move. + * + * @param sw + * @param port + * @param lastSeen + * @return + */ + protected boolean updateAttachmentPoint(DatapathId sw, OFPort port, + Date lastSeen) { + ITopologyService topology = deviceManager.topology; + List<AttachmentPoint> oldAPList; + List<AttachmentPoint> apList; + boolean oldAPFlag = false; + + if (!deviceManager.isValidAttachmentPoint(sw, port)) + return false; + AttachmentPoint newAP = new AttachmentPoint(sw, port, lastSeen); + // Copy the oldAP and ap list. + apList = new ArrayList<AttachmentPoint>(); + if (attachmentPoints != null) + apList.addAll(attachmentPoints); + oldAPList = new ArrayList<AttachmentPoint>(); + if (oldAPs != null) + oldAPList.addAll(oldAPs); + + // if the sw, port is in old AP, remove it from there + // and update the lastSeen in that object. + if (oldAPList.contains(newAP)) { + int index = oldAPList.indexOf(newAP); + newAP = oldAPList.remove(index); + newAP.setLastSeen(lastSeen); + this.oldAPs = oldAPList; + oldAPFlag = true; + } + + // newAP now contains the new attachment point. + + // Get the APMap is null or empty. + Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList); + if (apMap == null || apMap.isEmpty()) { + apList.add(newAP); + attachmentPoints = apList; + // there are no old attachment points - since the device exists, + // this + // may be because the host really moved (so the old AP port went + // down); + // or it may be because the switch restarted (so old APs were + // nullified). + // For now we will treat both cases as host moved. + return true; + } + + DatapathId id = topology.getL2DomainId(sw); + AttachmentPoint oldAP = apMap.get(id); + + if (oldAP == null) { // No attachment on this L2 domain. + apList = new ArrayList<AttachmentPoint>(); + apList.addAll(apMap.values()); + apList.add(newAP); + this.attachmentPoints = apList; + return true; // new AP found on an L2 island. + } + + // There is already a known attachment point on the same L2 island. + // we need to compare oldAP and newAP. + if (oldAP.equals(newAP)) { + // nothing to do here. just the last seen has to be changed. + if (newAP.lastSeen.after(oldAP.lastSeen)) { + oldAP.setLastSeen(newAP.lastSeen); + } + this.attachmentPoints = new ArrayList<AttachmentPoint>( + apMap.values()); + return false; // nothing to do here. + } + + int x = deviceManager.apComparator.compare(oldAP, newAP); + if (x < 0) { + // newAP replaces oldAP. + apMap.put(id, newAP); + this.attachmentPoints = new ArrayList<AttachmentPoint>( + apMap.values()); + + oldAPList = new ArrayList<AttachmentPoint>(); + if (oldAPs != null) + oldAPList.addAll(oldAPs); + oldAPList.add(oldAP); + this.oldAPs = oldAPList; + if (!topology.isInSameBroadcastDomain(oldAP.getSw(), + oldAP.getPort(), newAP.getSw(), newAP.getPort())) + return true; // attachment point changed. + } else if (oldAPFlag) { + // retain oldAP as is. Put the newAP in oldAPs for flagging + // possible duplicates. + oldAPList = new ArrayList<AttachmentPoint>(); + if (oldAPs != null) + oldAPList.addAll(oldAPs); + // Add to oldAPList only if it was picked up from the oldAPList + oldAPList.add(newAP); + this.oldAPs = oldAPList; + } + return false; + } + + /** + * Delete (sw,port) from the list of list of attachment points and oldAPs. + * + * @param sw + * @param port + * @return + */ + public boolean deleteAttachmentPoint(DatapathId sw, OFPort port) { + AttachmentPoint ap = new AttachmentPoint(sw, port, new Date(0)); + + if (this.oldAPs != null) { + ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>(); + apList.addAll(this.oldAPs); + int index = apList.indexOf(ap); + if (index > 0) { + apList.remove(index); + this.oldAPs = apList; + } + } + + if (this.attachmentPoints != null) { + ArrayList<AttachmentPoint> apList = new ArrayList<AttachmentPoint>(); + apList.addAll(this.attachmentPoints); + int index = apList.indexOf(ap); + if (index > 0) { + apList.remove(index); + this.attachmentPoints = apList; + return true; + } + } + return false; + } + + public boolean deleteAttachmentPoint(DatapathId sw) { + boolean deletedFlag; + ArrayList<AttachmentPoint> apList; + ArrayList<AttachmentPoint> modifiedList; + + // Delete the APs on switch sw in oldAPs. + deletedFlag = false; + apList = new ArrayList<AttachmentPoint>(); + if (this.oldAPs != null) + apList.addAll(this.oldAPs); + modifiedList = new ArrayList<AttachmentPoint>(); + + for (AttachmentPoint ap : apList) { + if (ap.getSw().equals(sw)) { + deletedFlag = true; + } else { + modifiedList.add(ap); + } + } + + if (deletedFlag) { + this.oldAPs = modifiedList; + } + + // Delete the APs on switch sw in attachmentPoints. + deletedFlag = false; + apList = new ArrayList<AttachmentPoint>(); + if (this.attachmentPoints != null) + apList.addAll(this.attachmentPoints); + modifiedList = new ArrayList<AttachmentPoint>(); + + for (AttachmentPoint ap : apList) { + if (ap.getSw().equals(sw)) { + deletedFlag = true; + } else { + modifiedList.add(ap); + } + } + + if (deletedFlag) { + this.attachmentPoints = modifiedList; + return true; + } + + return false; + } + + // ******* + // IDevice + // ******* + + @Override + public SwitchPort[] getOldAP() { + List<SwitchPort> sp = new ArrayList<SwitchPort>(); + SwitchPort[] returnSwitchPorts = new SwitchPort[] {}; + if (oldAPs == null) + return returnSwitchPorts; + if (oldAPs.isEmpty()) + return returnSwitchPorts; + + // copy ap list. + List<AttachmentPoint> oldAPList; + oldAPList = new ArrayList<AttachmentPoint>(); + + if (oldAPs != null) + oldAPList.addAll(oldAPs); + removeExpiredAttachmentPoints(oldAPList); + + if (oldAPList != null) { + for (AttachmentPoint ap : oldAPList) { + SwitchPort swport = new SwitchPort(ap.getSw(), ap.getPort()); + sp.add(swport); + } + } + return sp.toArray(new SwitchPort[sp.size()]); + } + + @Override + public SwitchPort[] getAttachmentPoints() { + return getAttachmentPoints(false); + } + + @Override + public SwitchPort[] getAttachmentPoints(boolean includeError) { + List<SwitchPort> sp = new ArrayList<SwitchPort>(); + SwitchPort[] returnSwitchPorts = new SwitchPort[] {}; + if (attachmentPoints == null) + return returnSwitchPorts; + if (attachmentPoints.isEmpty()) + return returnSwitchPorts; + + // copy ap list. + List<AttachmentPoint> apList = new ArrayList<AttachmentPoint>( + attachmentPoints); + + if (apList != null) { + for (AttachmentPoint ap : apList) { + SwitchPort swport = new SwitchPort(ap.getSw(), ap.getPort()); + sp.add(swport); + } + } + + if (!includeError) + return sp.toArray(new SwitchPort[sp.size()]); + + List<AttachmentPoint> oldAPList; + oldAPList = new ArrayList<AttachmentPoint>(); + + if (oldAPs != null) + oldAPList.addAll(oldAPs); + + if (removeExpiredAttachmentPoints(oldAPList)) + this.oldAPs = oldAPList; + + List<AttachmentPoint> dupList; + // get AP map. + Map<DatapathId, AttachmentPoint> apMap = getAPMap(apList); + dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap); + if (dupList != null) { + for (AttachmentPoint ap : dupList) { + SwitchPort swport = new SwitchPort(ap.getSw(), ap.getPort(), + ErrorStatus.DUPLICATE_DEVICE); + sp.add(swport); + } + } + return sp.toArray(new SwitchPort[sp.size()]); + } + + @Override + public Long getDeviceKey() { + return deviceKey; + } + + @Override + public MacAddress getMACAddress() { + // we assume only one MAC per device for now. + return entities[0].getMacAddress(); + } + + @Override + public String getMACAddressString() { + return macAddressString; + } + + @Override + public VlanVid[] getVlanId() { + return Arrays.copyOf(vlanIds, vlanIds.length); + } + + static final EnumSet<DeviceField> ipv4Fields = EnumSet.of(DeviceField.IPv4); + static final EnumSet<DeviceField> ipv6Fields = EnumSet.of(DeviceField.IPv6); + + @Override + public IPv4Address[] getIPv4Addresses() { + // XXX - TODO we can cache this result. Let's find out if this + // is really a performance bottleneck first though. + + TreeSet<IPv4Address> vals = new TreeSet<IPv4Address>(); + for (Entity e : entities) { + if (e.getIpv4Address().equals(IPv4Address.NONE)) + continue; + + // We have an IP address only if among the devices within the class + // we have the most recent entity with that IP. + boolean validIP = true; + Iterator<Device> devices = deviceManager.queryClassByEntity( + entityClass, ipv4Fields, e); + while (devices.hasNext()) { + Device d = devices.next(); + if (deviceKey.equals(d.getDeviceKey())) + continue; + for (Entity se : d.entities) { + if (se.getIpv4Address() != null + && se.getIpv4Address().equals(e.getIpv4Address()) + && !se.getLastSeenTimestamp() + .equals(Entity.NO_DATE) + && 0 < se.getLastSeenTimestamp().compareTo( + e.getLastSeenTimestamp())) { + validIP = false; + break; + } + } + if (!validIP) + break; + } + + if (validIP) + vals.add(e.getIpv4Address()); + } + + return vals.toArray(new IPv4Address[vals.size()]); + } + + @Override + public IPv6Address[] getIPv6Addresses() { + TreeSet<IPv6Address> vals = new TreeSet<IPv6Address>(); + for (Entity e : entities) { + if (e.getIpv6Address().equals(IPv6Address.NONE)) + continue; + + // We have an IP address only if among the devices within the class + // we have the most recent entity with that IP. + boolean validIP = true; + Iterator<Device> devices = deviceManager.queryClassByEntity( + entityClass, ipv6Fields, e); + while (devices.hasNext()) { + Device d = devices.next(); + if (deviceKey.equals(d.getDeviceKey())) + continue; + for (Entity se : d.entities) { + if (se.getIpv6Address() != null + && se.getIpv6Address().equals(e.getIpv6Address()) + && !se.getLastSeenTimestamp() + .equals(Entity.NO_DATE) + && 0 < se.getLastSeenTimestamp().compareTo( + e.getLastSeenTimestamp())) { + validIP = false; + break; + } + } + if (!validIP) + break; + } + + if (validIP) + vals.add(e.getIpv6Address()); + } + + return vals.toArray(new IPv6Address[vals.size()]); + } + + @Override + public VlanVid[] getSwitchPortVlanIds(SwitchPort swp) { + TreeSet<VlanVid> vals = new TreeSet<VlanVid>(); + for (Entity e : entities) { + if (e.switchDPID.equals(swp.getSwitchDPID()) + && e.switchPort.equals(swp.getPort())) { + if (e.getVlan() == null) + vals.add(VlanVid.ZERO); + else + vals.add(e.getVlan()); + } + } + return vals.toArray(new VlanVid[vals.size()]); + } + + @Override + public Date getLastSeen() { + Date d = null; + for (int i = 0; i < entities.length; i++) { + if (d == null + || entities[i].getLastSeenTimestamp().compareTo(d) > 0) + d = entities[i].getLastSeenTimestamp(); + } + return d; + } + + // *************** + // Getters/Setters + // *************** + + @Override + public IEntityClass getEntityClass() { + return entityClass; + } + + public Entity[] getEntities() { + return entities; + } + + public String getDHCPClientName() { + return dhcpClientName; + } + + // *************** + // Utility Methods + // *************** + + /** + * Check whether the device contains the specified entity + * + * @param entity + * the entity to search for + * @return the index of the entity, or <0 if not found + */ + protected int entityIndex(Entity entity) { + return Arrays.binarySearch(entities, entity); + } + + // ****** + // Object + // ****** + + @Override public int hashCode() { final int prime = 31; int result = 1; @@ -742,7 +859,7 @@ public class Device implements IDevice { return result; } - @Override + @Override public boolean equals(Object obj) { if (this == obj) return true; @@ -761,26 +878,34 @@ public class Device implements IDevice { return true; } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("Device [deviceKey="); - builder.append(deviceKey); - builder.append(", entityClass="); - builder.append(entityClass.getName()); - builder.append(", MAC="); - builder.append(macAddressString); - builder.append(", IPs=["); - boolean isFirst = true; - for (IPv4Address ip: getIPv4Addresses()) { - if (!isFirst) - builder.append(", "); - isFirst = false; - builder.append(ip.toString()); - } - builder.append("], APs="); - builder.append(Arrays.toString(getAttachmentPoints(true))); - builder.append("]"); - return builder.toString(); - } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Device [deviceKey="); + builder.append(deviceKey); + builder.append(", entityClass="); + builder.append(entityClass.getName()); + builder.append(", MAC="); + builder.append(macAddressString); + builder.append(", IPv4s=["); + boolean isFirst = true; + for (IPv4Address ip : getIPv4Addresses()) { + if (!isFirst) + builder.append(", "); + isFirst = false; + builder.append(ip.toString()); + } + builder.append("], IPv6s=["); + isFirst = true; + for (IPv6Address ip : getIPv6Addresses()) { + if (!isFirst) + builder.append(", "); + isFirst = false; + builder.append(ip.toString()); + } + builder.append("], APs="); + builder.append(Arrays.toString(getAttachmentPoints(true))); + builder.append("]"); + return builder.toString(); + } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java index 0c5100498a87c1c94736a90e793d1968c282283e..dd3702cc9ebbef8fe603e39209ee3d8a6ea27a3b 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndex.java @@ -114,4 +114,4 @@ public abstract class DeviceIndex { } } -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java index 2015bbe60d648d86c33a582d2c9bae9b5274e5c8..6404ba662173b70dc7ac291bd8a5dc368a1dc320 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIndexInterator.java @@ -55,5 +55,4 @@ public class DeviceIndexInterator implements Iterator<Device> { public void remove() { subIterator.remove(); } - -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java index 4c7c4a4b15b676b3aeef0308ceb35a44e964f139..199271d14506f25b173648e2741a97f20a1f491b 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceIterator.java @@ -23,6 +23,7 @@ import java.util.List; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -40,6 +41,7 @@ public class DeviceIterator extends FilterIterator<Device> { private MacAddress macAddress; private VlanVid vlan; private IPv4Address ipv4Address; + private IPv6Address ipv6Address; private DatapathId switchDPID; private OFPort switchPort; @@ -50,6 +52,7 @@ public class DeviceIterator extends FilterIterator<Device> { * @param macAddress The MAC address * @param vlan the VLAN * @param ipv4Address the ipv4 address + * @param ipv6Address the ipv6 address * @param switchDPID the switch DPID * @param switchPort the switch port */ @@ -58,6 +61,7 @@ public class DeviceIterator extends FilterIterator<Device> { MacAddress macAddress, VlanVid vlan, IPv4Address ipv4Address, + IPv6Address ipv6Address, DatapathId switchDPID, OFPort switchPort) { super(subIterator); @@ -66,6 +70,7 @@ public class DeviceIterator extends FilterIterator<Device> { this.macAddress = macAddress; this.vlan = vlan; this.ipv4Address = ipv4Address; + this.ipv6Address = ipv6Address; this.switchDPID = switchDPID; this.switchPort = switchPort; } @@ -86,35 +91,42 @@ public class DeviceIterator extends FilterIterator<Device> { } if (!match) return false; } - if (macAddress != null) { + if (!macAddress.equals(MacAddress.NONE)) { if (!macAddress.equals(value.getMACAddress())) return false; } - if (vlan != null) { + if (vlan != null) { /* VLAN is null, since VlanVid.ZERO is untagged */ VlanVid[] vlans = value.getVlanId(); List<VlanVid> searchableVlanList = Arrays.asList(vlans); if (!searchableVlanList.contains(vlan)) { return false; } } - if (ipv4Address != null) { + if (!ipv4Address.equals(IPv4Address.NONE)) { IPv4Address[] ipv4Addresses = value.getIPv4Addresses(); List<IPv4Address> searchableIPv4AddrList = Arrays.asList(ipv4Addresses); if (!searchableIPv4AddrList.contains(ipv4Address)) { return false; } } - if (switchDPID != null || switchPort != null) { + if (!ipv6Address.equals(IPv6Address.NONE)) { + IPv6Address[] ipv6Addresses = value.getIPv6Addresses(); + List<IPv6Address> searchableIPv6AddrList = Arrays.asList(ipv6Addresses); + if (!searchableIPv6AddrList.contains(ipv6Address)) { + return false; + } + } + if (!switchDPID.equals(DatapathId.NONE) || !switchPort.equals(OFPort.ZERO)) { SwitchPort[] sps = value.getAttachmentPoints(); if (sps == null) return false; match = false; for (SwitchPort sp : sps) { - if (switchDPID != null) { + if (!switchDPID.equals(DatapathId.NONE)) { if (!switchDPID.equals(sp.getSwitchDPID())) return false; } - if (switchPort != null) { + if (!switchPort.equals(OFPort.ZERO)) { if (!switchPort.equals(sp.getPort())) return false; } @@ -125,4 +137,4 @@ public class DeviceIterator extends FilterIterator<Device> { } return true; } -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 7f9c023042ca47cb8780e635c66ad5aab94b32cd..cb5c0fcb25c22be4fbd76d03ac26a1a922cd3d5a 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -39,6 +39,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.HAListenerTypeMarker; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -76,6 +78,7 @@ import net.floodlightcontroller.packet.DHCP; import net.floodlightcontroller.packet.DHCPOption; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.IPv6; import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.packet.DHCP.DHCPOptionCode; import net.floodlightcontroller.restserver.IRestApiService; @@ -92,6 +95,7 @@ import org.projectfloodlight.openflow.protocol.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -240,7 +244,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT * The entity classifier currently in use */ protected IEntityClassifierService entityClassifier; - + /** * Used to cache state about specific entity classes */ @@ -360,7 +364,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT if (oldDomain.getLong() < newDomain.getLong()) return -1; else if (oldDomain.getLong() > newDomain.getLong()) return 1; - // Give preference to LOCAL always if (oldPort != OFPort.LOCAL && newPort == OFPort.LOCAL) { @@ -373,7 +376,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT // We expect that the last seen of the new AP is higher than // old AP, if it is not, just reverse and send the negative // of the result. - if (oldAP.getActiveSince().after(newAP.getActiveSince())) + if (oldAP.getLastSeen().after(newAP.getLastSeen())) //TODO should this be lastSeen? @Ryan did change this from activeSince return -compare(newAP, oldAP); long activeOffset = 0; @@ -440,16 +443,37 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT } @Override - public IDevice findDevice(MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, DatapathId switchDPID, - OFPort switchPort) + public IDevice findDevice(@Nonnull MacAddress macAddress, VlanVid vlan, + @Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address, + @Nonnull DatapathId switchDPID, @Nonnull OFPort switchPort) throws IllegalArgumentException { - if (vlan != null && vlan.getVlan() <= 0) - vlan = null; - if (ipv4Address != null && ipv4Address.getInt() == 0) - ipv4Address = null; - Entity e = new Entity(macAddress, vlan, ipv4Address, switchDPID, - switchPort, null); + if (macAddress == null) { + throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'"); + } + if (ipv4Address == null) { + throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'"); + } + if (ipv6Address == null) { + throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'"); + } + if (vlan == null) { + throw new IllegalArgumentException("VLAN cannot be null. Try VlanVid.ZERO if intention is 'no VLAN / untagged'"); + } + if (switchDPID == null) { + throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'"); + } + if (switchPort == null) { + throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'"); + } + + Entity e = new Entity(macAddress, vlan, + ipv4Address, ipv6Address, + switchDPID, switchPort, Entity.NO_DATE); + + /* + * allKeyFieldsPresent() will check if the entity key fields (e.g. MAC and VLAN) + * have non-"zero" values i.e. are not set to e.g. MacAddress.NONE and VlanVid.ZERO + */ if (!allKeyFieldsPresent(e, entityClassifier.getKeyFields())) { throw new IllegalArgumentException("Not all key fields specified." + " Required fields: " + entityClassifier.getKeyFields()); @@ -458,17 +482,27 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT } @Override - public IDevice findClassDevice(IEntityClass entityClass, MacAddress macAddress, - VlanVid vlan, IPv4Address ipv4Address) + public IDevice findClassDevice(@Nonnull IEntityClass entityClass, @Nonnull MacAddress macAddress, + @Nonnull VlanVid vlan, @Nonnull IPv4Address ipv4Address, @Nonnull IPv6Address ipv6Address) throws IllegalArgumentException { - if (vlan != null && vlan.getVlan() <= 0) - vlan = null; - if (ipv4Address != null && ipv4Address.getInt() == 0) - ipv4Address = null; - Entity e = new Entity(macAddress, vlan, ipv4Address, - null, null, null); - if (entityClass == null || - !allKeyFieldsPresent(e, entityClass.getKeyFields())) { + if (entityClass == null) { + throw new IllegalArgumentException("Entity class cannot be null."); + } + if (macAddress == null) { + throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'"); + } + if (ipv4Address == null) { + throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'"); + } + if (ipv6Address == null) { + throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'"); + } + if (vlan == null) { + throw new IllegalArgumentException("VLAN cannot be null. Try VlanVid.ZERO if intention is 'no VLAN / untagged'"); + } + + Entity e = new Entity(macAddress, vlan, ipv4Address, ipv6Address, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE); + if (!allKeyFieldsPresent(e, entityClass.getKeyFields())) { throw new IllegalArgumentException("Not all key fields and/or " + " no source device specified. Required fields: " + entityClassifier.getKeyFields()); @@ -493,15 +527,33 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT } @Override - public Iterator<? extends IDevice> queryDevices(MacAddress macAddress, + public Iterator<? extends IDevice> queryDevices(@Nonnull MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + @Nonnull IPv4Address ipv4Address, + @Nonnull IPv6Address ipv6Address, + @Nonnull DatapathId switchDPID, + @Nonnull OFPort switchPort) { + if (macAddress == null) { + throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'"); + } + if (ipv4Address == null) { + throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'"); + } + if (ipv6Address == null) { + throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'"); + } + /* VLAN can be null in this case, which means 'don't care' */ + if (switchDPID == null) { + throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'"); + } + if (switchPort == null) { + throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'"); + } + DeviceIndex index = null; if (secondaryIndexMap.size() > 0) { EnumSet<DeviceField> keys = - getEntityKeys(macAddress, vlan, ipv4Address, + getEntityKeys(macAddress, vlan, ipv4Address, ipv6Address, switchDPID, switchPort); index = secondaryIndexMap.get(keys); } @@ -515,9 +567,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT Entity entity = new Entity(macAddress, vlan, ipv4Address, + ipv6Address, switchDPID, switchPort, - null); + Entity.NO_DATE); deviceIterator = new DeviceIndexInterator(this, index.queryByEntity(entity)); } @@ -528,18 +581,37 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT macAddress, vlan, ipv4Address, + ipv6Address, switchDPID, switchPort); return di; } @Override - public Iterator<? extends IDevice> queryClassDevices(IEntityClass entityClass, - MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + public Iterator<? extends IDevice> queryClassDevices(@Nonnull IEntityClass entityClass, + @Nonnull MacAddress macAddress, + @Nonnull VlanVid vlan, + @Nonnull IPv4Address ipv4Address, + @Nonnull IPv6Address ipv6Address, + @Nonnull DatapathId switchDPID, + @Nonnull OFPort switchPort) { + if (macAddress == null) { + throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'"); + } + if (ipv4Address == null) { + throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'"); + } + if (ipv6Address == null) { + throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'"); + } + /* VLAN can be null, which means 'don't care' */ + if (switchDPID == null) { + throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'"); + } + if (switchPort == null) { + throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'"); + } + ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>(); ClassState classState = getClassState(entityClass); @@ -548,7 +620,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT if (classState.secondaryIndexMap.size() > 0) { EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan, ipv4Address, - switchDPID, switchPort); + ipv6Address, switchDPID, switchPort); index = classState.secondaryIndexMap.get(keys); } @@ -560,7 +632,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT return new DeviceIterator(deviceMap.values().iterator(), new IEntityClass[] { entityClass }, macAddress, vlan, ipv4Address, - switchDPID, switchPort); + ipv6Address, switchDPID, switchPort); } else { // scan the entire class iter = new DeviceIndexInterator(this, index.getAll()); @@ -571,9 +643,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT new Entity(macAddress, vlan, ipv4Address, + ipv6Address, switchDPID, switchPort, - null); + Entity.NO_DATE); iter = new DeviceIndexInterator(this, index.queryByEntity(entity)); } @@ -582,16 +655,34 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT return new MultiIterator<Device>(iterators.iterator()); } - protected Iterator<Device> getDeviceIteratorForQuery(MacAddress macAddress, + protected Iterator<Device> getDeviceIteratorForQuery(@Nonnull MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { + @Nonnull IPv4Address ipv4Address, + @Nonnull IPv6Address ipv6Address, + @Nonnull DatapathId switchDPID, + @Nonnull OFPort switchPort) { + if (macAddress == null) { + throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'"); + } + if (ipv4Address == null) { + throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'"); + } + if (ipv6Address == null) { + throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'"); + } + /* VLAN can be null, which means 'don't care' */ + if (switchDPID == null) { + throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'"); + } + if (switchPort == null) { + throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'"); + } + DeviceIndex index = null; if (secondaryIndexMap.size() > 0) { EnumSet<DeviceField> keys = getEntityKeys(macAddress, vlan, ipv4Address, - switchDPID, switchPort); + ipv6Address, switchDPID, switchPort); index = secondaryIndexMap.get(keys); } @@ -604,9 +695,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT Entity entity = new Entity(macAddress, vlan, ipv4Address, + ipv6Address, switchDPID, switchPort, - null); + Entity.NO_DATE); deviceIterator = new DeviceIndexInterator(this, index.queryByEntity(entity)); } @@ -617,6 +709,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT macAddress, vlan, ipv4Address, + ipv6Address, switchDPID, switchPort); return di; @@ -694,6 +787,11 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT public void deviceIPV4AddrChanged(IDevice device) { generateDeviceEvent(device, "host-ipv4-addr-changed"); } + + @Override + public void deviceIPV6AddrChanged(IDevice device) { + generateDeviceEvent(device, "host-ipv6-addr-changed"); + } @Override public void deviceVlanChanged(IDevice device) { @@ -703,6 +801,8 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT private void generateDeviceEvent(IDevice device, String reason) { List<IPv4Address> ipv4Addresses = new ArrayList<IPv4Address>(Arrays.asList(device.getIPv4Addresses())); + List<IPv6Address> ipv6Addresses = + new ArrayList<IPv6Address>(Arrays.asList(device.getIPv6Addresses())); List<SwitchPort> oldAps = new ArrayList<SwitchPort>(Arrays.asList(device.getOldAP())); List<SwitchPort> currentAps = @@ -712,6 +812,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT debugEventCategory.newEventNoFlush(new DeviceEvent(device.getMACAddress(), ipv4Addresses, + ipv6Addresses, oldAps, currentAps, vlanIds, reason)); @@ -807,7 +908,8 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT public void init(FloodlightModuleContext fmc) throws FloodlightModuleException { this.perClassIndices = new HashSet<EnumSet<DeviceField>>(); - addIndex(true, EnumSet.of(DeviceField.IPV4)); + addIndex(true, EnumSet.of(DeviceField.IPv4)); + addIndex(true, EnumSet.of(DeviceField.IPv6)); this.deviceListeners = new ListenerDispatcher<String, IDeviceListener>(); this.suppressAPs = Collections.newSetFromMap( @@ -1055,7 +1157,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT @Override public boolean isCallbackOrderingPostreq(HAListenerTypeMarker type, String name) { - // TODO Auto-generated method stub return false; } @@ -1173,22 +1274,37 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT } /** - * Get sender IP address from packet if the packet is an ARP + * Get sender IPv4 address from packet if the packet is an ARP * packet and if the source MAC address matches the ARP packets * sender MAC address. * @param eth * @param dlAddr * @return */ - private IPv4Address getSrcNwAddr(Ethernet eth, MacAddress dlAddr) { + private IPv4Address getSrcIPv4AddrFromARP(Ethernet eth, MacAddress dlAddr) { if (eth.getPayload() instanceof ARP) { ARP arp = (ARP) eth.getPayload(); - if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) && (MacAddress.of(arp.getSenderHardwareAddress()).equals(dlAddr))) { - return IPv4Address.of(arp.getSenderProtocolAddress()); + if ((arp.getProtocolType() == ARP.PROTO_TYPE_IP) && (arp.getSenderHardwareAddress().equals(dlAddr))) { + return arp.getSenderProtocolAddress(); } } return IPv4Address.NONE; } + + /** + * Get sender IPv6 address from packet if the packet is ND + * + * @param eth + * @param dlAddr + * @return + */ + private IPv6Address getSrcIPv6Addr(Ethernet eth) { + if (eth.getPayload() instanceof IPv6) { + IPv6 ipv6 = (IPv6) eth.getPayload(); + return ipv6.getSourceAddress(); + } + return IPv6Address.NONE; + } /** * Parse an entity from an {@link Ethernet} packet. @@ -1208,10 +1324,12 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT return null; VlanVid vlan = VlanVid.ofVlan(eth.getVlanID()); - IPv4Address nwSrc = getSrcNwAddr(eth, dlAddr); + IPv4Address ipv4Src = getSrcIPv4AddrFromARP(eth, dlAddr); + IPv6Address ipv6Src = ipv4Src.equals(IPv4Address.NONE) ? getSrcIPv6Addr(eth) : IPv6Address.NONE; return new Entity(dlAddr, vlan, - nwSrc, + ipv4Src, + ipv6Src, swdpid, port, new Date()); @@ -1231,7 +1349,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT MacAddress dlAddr = eth.getSourceMACAddress(); - MacAddress senderAddr = MacAddress.of(arp.getSenderHardwareAddress()); + MacAddress senderAddr = arp.getSenderHardwareAddress(); if (dlAddr.equals(senderAddr)) return; // arp request @@ -1239,15 +1357,16 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT if (senderAddr.isBroadcast() || senderAddr.isMulticast()) return; // Ignore zero sender mac - if (senderAddr.getLong() == 0) + if (senderAddr.equals(MacAddress.of(0))) return; VlanVid vlan = VlanVid.ofVlan(eth.getVlanID()); - IPv4Address nwSrc = IPv4Address.of(arp.getSenderProtocolAddress()); + IPv4Address nwSrc = arp.getSenderProtocolAddress(); Entity e = new Entity(senderAddr, - ((vlan.getVlan() >= 0) ? vlan : null), - ((nwSrc.getInt() != 0) ? nwSrc : null), + vlan, /* will either be a valid tag or VlanVid.ZERO if untagged */ + nwSrc, + IPv6Address.NONE, /* must be none for ARP */ swdpid, port, new Date()); @@ -1263,26 +1382,31 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT protected Entity getDestEntityFromPacket(Ethernet eth) { MacAddress dlAddr = eth.getDestinationMACAddress(); VlanVid vlan = VlanVid.ofVlan(eth.getVlanID()); - IPv4Address nwDst = IPv4Address.NONE; + IPv4Address ipv4Dst = IPv4Address.NONE; + IPv6Address ipv6Dst = IPv6Address.NONE; // Ignore broadcast/multicast destination if (dlAddr.isBroadcast() || dlAddr.isMulticast()) return null; // Ignore zero dest mac - if (dlAddr.getLong() == 0) + if (dlAddr.equals(MacAddress.of(0))) return null; if (eth.getPayload() instanceof IPv4) { IPv4 ipv4 = (IPv4) eth.getPayload(); - nwDst = ipv4.getDestinationAddress(); + ipv4Dst = ipv4.getDestinationAddress(); + } else if (eth.getPayload() instanceof IPv6) { + IPv6 ipv6 = (IPv6) eth.getPayload(); + ipv6Dst = ipv6.getDestinationAddress(); } return new Entity(dlAddr, vlan, - nwDst, - null, - null, - null); + ipv4Dst, + ipv6Dst, + DatapathId.NONE, + OFPort.ZERO, + Entity.NO_DATE); } /** @@ -1497,12 +1621,13 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT cntPacketOnInternalPortForKnownDevice.increment(); break; } + int entityindex = -1; if ((entityindex = device.entityIndex(entity)) >= 0) { // Entity already exists // update timestamp on the found entity Date lastSeen = entity.getLastSeenTimestamp(); - if (lastSeen == null) { + if (lastSeen.equals(Entity.NO_DATE)) { lastSeen = new Date(); entity.setLastSeenTimestamp(lastSeen); } @@ -1553,11 +1678,9 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT boolean moved = device.updateAttachmentPoint(entity.getSwitchDPID(), entity.getSwitchPort(), entity.getLastSeenTimestamp()); - // TODO: use update mechanism instead of sending the - // notification directly if (moved) { // we count device moved events in sendDeviceMovedNotification() - sendDeviceMovedNotification(device); + // TODO remove this. It's now done in the event handler as a result of the update above... sendDeviceMovedNotification(device); if (logger.isTraceEnabled()) { logger.trace("Device moved: attachment points {}," + "entities {}", device.attachmentPoints, @@ -1591,45 +1714,51 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT return true; } - - - - protected EnumSet<DeviceField> findChangedFields(Device device, Entity newEntity) { EnumSet<DeviceField> changedFields = - EnumSet.of(DeviceField.IPV4, + EnumSet.of(DeviceField.IPv4, + DeviceField.IPv6, DeviceField.VLAN, DeviceField.SWITCH); - if (newEntity.getIpv4Address() == null) - changedFields.remove(DeviceField.IPV4); - if (newEntity.getVlan() == null) + /* + * Do we really need this here? + * + if (newEntity.getIpv4Address().equals(IPv4Address.NONE)) + changedFields.remove(DeviceField.IPv4); + if (newEntity.getIpv6Address().equals(IPv6Address.NONE)) + changedFields.remove(DeviceField.IPv6); + /*if (newEntity.getVlan().equals(VlanVid.ZERO)) TODO VLAN is ZERO here, since the actual Device and Entity must have some sort of VLAN, either untagged (ZERO) or some value changedFields.remove(DeviceField.VLAN); - if (newEntity.getSwitchDPID() == null || - newEntity.getSwitchPort() == null) - changedFields.remove(DeviceField.SWITCH); + if (newEntity.getSwitchDPID().equals(DatapathId.NONE) || + newEntity.getSwitchPort().equals(OFPort.ZERO)) + changedFields.remove(DeviceField.SWITCH); - if (changedFields.size() == 0) return changedFields; + if (changedFields.size() == 0) return changedFields; */ for (Entity entity : device.getEntities()) { - if (newEntity.getIpv4Address() == null || - (entity.getIpv4Address() != null && - entity.getIpv4Address().equals(newEntity.getIpv4Address()))) - changedFields.remove(DeviceField.IPV4); - if (newEntity.getVlan() == null || - (entity.getVlan() != null && - entity.getVlan().equals(newEntity.getVlan()))) + if (newEntity.getIpv4Address().equals(IPv4Address.NONE) || /* NONE means 'not in this packet' */ + entity.getIpv4Address().equals(newEntity.getIpv4Address())) /* these (below) might be defined and if they are and changed, then the device has changed */ + changedFields.remove(DeviceField.IPv4); + if (newEntity.getIpv6Address().equals(IPv6Address.NONE) || /* NONE means 'not in this packet' */ + entity.getIpv6Address().equals(newEntity.getIpv6Address())) + changedFields.remove(DeviceField.IPv6); + if (entity.getVlan().equals(newEntity.getVlan())) /* these (below) must be defined in each and every packet-in, and if different signal a device field change */ changedFields.remove(DeviceField.VLAN); - if (newEntity.getSwitchDPID() == null || - newEntity.getSwitchPort() == null || - (entity.getSwitchDPID() != null && - entity.getSwitchPort() != null && - entity.getSwitchDPID().equals(newEntity.getSwitchDPID()) && + if (newEntity.getSwitchDPID().equals(DatapathId.NONE) || + newEntity.getSwitchPort().equals(OFPort.ZERO) || + (entity.getSwitchDPID().equals(newEntity.getSwitchDPID()) && entity.getSwitchPort().equals(newEntity.getSwitchPort()))) changedFields.remove(DeviceField.SWITCH); } + if (changedFields.contains(DeviceField.SWITCH)) { + if (!isValidAttachmentPoint(newEntity.getSwitchDPID(), newEntity.getSwitchPort())) { + changedFields.remove(DeviceField.SWITCH); + } + } + return changedFields; } @@ -1669,12 +1798,15 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT case CHANGE: for (DeviceField field : update.fieldsChanged) { switch (field) { - case IPV4: + case IPv4: listener.deviceIPV4AddrChanged(update.device); break; + case IPv6: + listener.deviceIPV6AddrChanged(update.device); + break; case SWITCH: case PORT: - //listener.deviceMoved(update.device); + listener.deviceMoved(update.device); // TODO why was this commented out? break; case VLAN: listener.deviceVlanChanged(update.device); @@ -1702,19 +1834,26 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT case MAC: // MAC address is always present break; - case IPV4: - if (e.ipv4Address == null) return false; + case IPv4: + case IPv6: + if (e.ipv4Address.equals(IPv4Address.NONE) && e.ipv6Address.equals(IPv6Address.NONE)) { + return false; // mutually exclusive + } break; case SWITCH: - if (e.switchDPID == null) return false; + if (e.switchDPID.equals(DatapathId.NONE)) { + return false; + } break; case PORT: - if (e.switchPort == null) return false; + if (e.switchPort.equals(OFPort.ZERO)) { + return false; + } break; case VLAN: - // FIXME: vlan==null is ambiguous: it can mean: not present - // or untagged - //if (e.vlan == null) return false; + if (e.vlan == null) { /* VLAN is null for 'don't care' or 'unspecified'. It's VlanVid.ZERO for untagged. */ + return false; /* For key field of VLAN, the VLAN **MUST** be set to either ZERO or some value. */ + } break; default: // we should never get here. unless somebody extended @@ -1819,7 +1958,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT toRemove.clear(); toKeep.clear(); for (Entity e : d.getEntities()) { - if (e.getLastSeenTimestamp() != null && + if (!e.getLastSeenTimestamp().equals(Entity.NO_DATE) && 0 > e.getLastSeenTimestamp().compareTo(cutoff)) { // individual entity needs to be removed toRemove.add(e); @@ -1937,20 +2076,19 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT } } - private EnumSet<DeviceField> getEntityKeys(MacAddress macAddress, - VlanVid vlan, - IPv4Address ipv4Address, - DatapathId switchDPID, - OFPort switchPort) { - // FIXME: vlan==null is a valid search. Need to handle this - // case correctly. Note that the code will still work correctly. - // But we might do a full device search instead of using an index. + private EnumSet<DeviceField> getEntityKeys(@Nonnull MacAddress macAddress, + VlanVid vlan, /* A null VLAN means 'don't care'; VlanVid.ZERO means 'untagged' */ + @Nonnull IPv4Address ipv4Address, + @Nonnull IPv6Address ipv6Address, + @Nonnull DatapathId switchDPID, + @Nonnull OFPort switchPort) { EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class); - if (macAddress != null) keys.add(DeviceField.MAC); - if (vlan != null) keys.add(DeviceField.VLAN); - if (ipv4Address != null) keys.add(DeviceField.IPV4); - if (switchDPID != null) keys.add(DeviceField.SWITCH); - if (switchPort != null) keys.add(DeviceField.PORT); + if (!macAddress.equals(MacAddress.NONE)) keys.add(DeviceField.MAC); + if (vlan != null) keys.add(DeviceField.VLAN); /* TODO verify fix. null means 'don't care' and will conduct full search; VlanVid.ZERO means 'untagged' and only uses untagged index */ + if (!ipv4Address.equals(IPv4Address.NONE)) keys.add(DeviceField.IPv4); + if (!ipv6Address.equals(IPv6Address.NONE)) keys.add(DeviceField.IPv6); + if (!switchDPID.equals(DatapathId.NONE)) keys.add(DeviceField.SWITCH); + if (!switchPort.equals(OFPort.ZERO)) keys.add(DeviceField.PORT); return keys; } @@ -1969,7 +2107,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT return new Device(this, deviceKey, entity, entityClass); } - // TODO: FIX THIS. + // TODO: FIX THIS. What's 'this' that needs fixing? protected Device allocateDevice(Long deviceKey, String dhcpClientName, List<AttachmentPoint> aps, @@ -1986,7 +2124,8 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT return new Device(device, entity, insertionpoint); } - //not used + //not used + /* TODO then let's get rid of it? protected Device allocateDevice(Device device, Set <Entity> entities) { List <AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>(); @@ -2014,7 +2153,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT entities, device.getEntityClass()); d.updateAttachmentPoint(); return d; - } + } */ // ********************* // ITopologyListener @@ -2033,7 +2172,6 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT } } } - while (diter.hasNext()) { Device d = diter.next(); if (d.updateAttachmentPoint()) { @@ -2353,6 +2491,7 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT // Do we have a device for this entity?? IDevice d = findDevice(MacAddress.of(se.macAddress), VlanVid.ofVlan(se.vlan), IPv4Address.of(se.ipv4Address), + IPv6Address.NONE, DatapathId.of(se.switchDPID), OFPort.of(se.switchPort)); if (d != null) { @@ -2408,8 +2547,10 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT private class DeviceEvent { @EventColumn(name = "MAC", description = EventFieldType.MAC) private final MacAddress macAddress; - @EventColumn(name = "IPs", description = EventFieldType.IPv4) + @EventColumn(name = "IPv4s", description = EventFieldType.IPv4) private final List<IPv4Address> ipv4Addresses; + @EventColumn(name = "IPv6s", description = EventFieldType.IPv6) + private final List<IPv6Address> ipv6Addresses; @EventColumn(name = "Old Attachment Points", description = EventFieldType.COLLECTION_ATTACHMENT_POINT) private final List<SwitchPort> oldAttachmentPoints; @@ -2422,16 +2563,18 @@ public class DeviceManagerImpl implements IDeviceService, IOFMessageListener, IT private final String reason; public DeviceEvent(MacAddress macAddress, List<IPv4Address> ipv4Addresses, + List<IPv6Address> ipv6Addresses, List<SwitchPort> oldAttachmentPoints, List<SwitchPort> currentAttachmentPoints, List<VlanVid> vlanIds, String reason) { super(); this.macAddress = macAddress; this.ipv4Addresses = ipv4Addresses; + this.ipv6Addresses = ipv6Addresses; this.oldAttachmentPoints = oldAttachmentPoints; this.currentAttachmentPoints = currentAttachmentPoints; this.vlanIds = vlanIds; this.reason = reason; } } -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java index df47d32b2558df200cf8f02154cbde6444411ff4..a603696054c8339b906fed8256411dc818c5d5aa 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceMultiIndex.java @@ -78,7 +78,7 @@ public class DeviceMultiIndex extends DeviceIndex { Collection<Long> devices = null; IndexedEntity ie = new IndexedEntity(keyFields, entity); - if (!ie.hasNonNullKeys()) return false; + if (!ie.hasNonZeroOrNonNullKeys()) return false; devices = index.get(ie); if (devices == null) { @@ -106,4 +106,4 @@ public class DeviceMultiIndex extends DeviceIndex { if (devices != null) devices.remove(deviceKey); } -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java index 82fb832e5d7049fc0342b7f7a10caabcb0891567..d0846c2a25abf2be1cb3323888fe64714e0c4f3d 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceSyncRepresentation.java @@ -9,6 +9,7 @@ import java.util.List; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -19,152 +20,160 @@ import net.floodlightcontroller.devicemanager.SwitchPort; import com.fasterxml.jackson.annotation.JsonProperty; public class DeviceSyncRepresentation { - public static class SyncEntity implements Comparable<SyncEntity> { - @JsonProperty - public long macAddress; - @JsonProperty - public int ipv4Address; - @JsonProperty - public short vlan; - @JsonProperty - public long switchDPID; - @JsonProperty - public int switchPort; - @JsonProperty - public Date lastSeenTimestamp; - @JsonProperty - public Date activeSince; - - public SyncEntity() { - // do nothing; - } - - public SyncEntity(Entity e) { - this.macAddress = (e.getMacAddress() != null ? e.getMacAddress().getLong() : 0); - this.ipv4Address = (e.getIpv4Address() != null ? e.getIpv4Address().getInt() : 0); - this.vlan = (e.getVlan() != null ? e.getVlan().getVlan() : -1); - this.switchDPID = (e.getSwitchDPID() != null ? e.getSwitchDPID().getLong() : 0); - this.switchPort = (e.getSwitchPort() != null ? e.getSwitchPort().getPortNumber() : 0); - if (e.getLastSeenTimestamp() == null) - this.lastSeenTimestamp = null; - else - this.lastSeenTimestamp = new Date(e.getLastSeenTimestamp().getTime()); - if (e.getActiveSince() == null) - this.activeSince = null; - else - this.activeSince = new Date(e.getActiveSince().getTime()); - } - - public Entity asEntity() { - Entity e = new Entity(macAddress == 0 ? null : MacAddress.of(macAddress), - vlan == -1 ? null : VlanVid.ofVlan(vlan), - ipv4Address == 0 ? null : IPv4Address.of(ipv4Address), - switchDPID == 0 ? null : DatapathId.of(switchDPID), - switchPort == 0 ? null : OFPort.of(switchPort), - lastSeenTimestamp); - e.setActiveSince(activeSince); - return e; - } - - @Override - public int compareTo(SyncEntity other) { - return lastSeenTimestamp.compareTo(other.lastSeenTimestamp); - } - - @Override - public String toString() { - return asEntity().toString(); - } - } - - private String key; - private List<SyncEntity> entities; - - public DeviceSyncRepresentation() { - // do nothing - } - - public DeviceSyncRepresentation(Device device) { - this.key = computeKey(device); - this.entities = new ArrayList<SyncEntity>(); - // FIXME: do we need the APs with errors as well?? - // FIXME - SwitchPort[] aps = device.getAttachmentPoints(); - for(Entity e: device.getEntities()) { - // Add the entities from the device only if they either don't - // have a switch/port or if they are an attachment point or - // if they have an IP address. - if (!e.hasSwitchPort()) { - this.entities.add(new SyncEntity(e)); - } else if (isAttachmentPointEntity(aps, e)) { - this.entities.add(new SyncEntity(e)); - } else if (e.getIpv4Address() != null) { - this.entities.add(new SyncEntity(e)); - } - } - Collections.sort(this.entities); - } - - private static boolean isAttachmentPointEntity(SwitchPort[] aps, Entity e) { - if (!e.hasSwitchPort()) - return false; - for (SwitchPort p: aps) { - if (e.getSwitchDPID().equals(p.getSwitchDPID()) && - e.getSwitchPort().equals(p.getPort())) { - return true; - } - } - return false; - } - - static String computeKey(Device d) { - StringBuilder bld = new StringBuilder(d.getEntityClass().getName()); - bld.append("::"); - EnumSet<DeviceField> keyFields = d.getEntityClass().getKeyFields(); - if (keyFields.contains(DeviceField.MAC)) { - bld.append(d.getMACAddressString()); - bld.append("::"); - } - if (keyFields.contains(DeviceField.VLAN)) { - if (d.getVlanId() != null) - bld.append(Arrays.toString(d.getVlanId())); - bld.append("::"); - } - if (keyFields.contains(DeviceField.SWITCH) || - keyFields.contains(DeviceField.PORT) ) { - if (d.getAttachmentPoints(true) != null) - bld.append(Arrays.toString(d.getAttachmentPoints(true))); - bld.append("::"); - } - if (keyFields.contains(DeviceField.IPV4)) { - if (d.getIPv4Addresses() != null) - bld.append(Arrays.toString(d.getIPv4Addresses())); - bld.append("::"); - } - return bld.toString(); - } - - public String getKey() { - return key; - } - public void setKey(String key) { - this.key = key; - } - public List<SyncEntity> getEntities() { - return entities; - } - public void setEntities(List<SyncEntity> entities) { - if (entities == null) { - this.entities = null; - } else { - List<SyncEntity> tmp = new ArrayList<SyncEntity>(entities); - Collections.sort(tmp); - this.entities = tmp; - } - } - - @Override - public String toString() { - return key; - } + public static class SyncEntity implements Comparable<SyncEntity> { + @JsonProperty + public long macAddress; + @JsonProperty + public int ipv4Address; // TODO Sync IPv6 address + @JsonProperty + public short vlan; + @JsonProperty + public long switchDPID; + @JsonProperty + public int switchPort; + @JsonProperty + public Date lastSeenTimestamp; + @JsonProperty + public Date activeSince; + + public SyncEntity() { + // do nothing; + } + + public SyncEntity(Entity e) { + this.macAddress = (e.getMacAddress() != null ? e.getMacAddress().getLong() : 0); + this.ipv4Address = (e.getIpv4Address() != null ? e.getIpv4Address().getInt() : 0); + this.vlan = (e.getVlan() != null ? e.getVlan().getVlan() : -1); + this.switchDPID = (e.getSwitchDPID() != null ? e.getSwitchDPID().getLong() : 0); + this.switchPort = (e.getSwitchPort() != null ? e.getSwitchPort().getPortNumber() : 0); + if (e.getLastSeenTimestamp() == null) + this.lastSeenTimestamp = null; + else + this.lastSeenTimestamp = new Date(e.getLastSeenTimestamp().getTime()); + if (e.getActiveSince() == null) + this.activeSince = null; + else + this.activeSince = new Date(e.getActiveSince().getTime()); + } + + public Entity asEntity() { + Entity e = new Entity(MacAddress.of(macAddress), + VlanVid.ofVlan(vlan), + IPv4Address.of(ipv4Address), + IPv6Address.NONE, + DatapathId.of(switchDPID), + OFPort.of(switchPort), + lastSeenTimestamp); + e.setActiveSince(activeSince); + return e; + } + + @Override + public int compareTo(SyncEntity other) { + return lastSeenTimestamp.compareTo(other.lastSeenTimestamp); + } + + @Override + public String toString() { + return asEntity().toString(); + } + } + + private String key; + private List<SyncEntity> entities; + + public DeviceSyncRepresentation() { + // do nothing + } + + public DeviceSyncRepresentation(Device device) { + this.key = computeKey(device); + this.entities = new ArrayList<SyncEntity>(); + // FIXME: do we need the APs with errors as well?? + // FIXME + SwitchPort[] aps = device.getAttachmentPoints(); + for (Entity e : device.getEntities()) { + // Add the entities from the device only if they either don't + // have a switch/port or if they are an attachment point or + // if they have an IP address. + if (!e.hasSwitchPort()) { + this.entities.add(new SyncEntity(e)); + } else if (isAttachmentPointEntity(aps, e)) { + this.entities.add(new SyncEntity(e)); + } else if (!e.getIpv4Address().equals(IPv4Address.NONE)) { + this.entities.add(new SyncEntity(e)); + } + } + Collections.sort(this.entities); + } + + private static boolean isAttachmentPointEntity(SwitchPort[] aps, Entity e) { + if (!e.hasSwitchPort()) + return false; + for (SwitchPort p : aps) { + if (e.getSwitchDPID().equals(p.getSwitchDPID()) && + e.getSwitchPort().equals(p.getPort()) + && (!e.getSwitchDPID().equals(DatapathId.NONE) || + !e.getSwitchPort().equals(OFPort.ZERO))) { + return true; + } + } + return false; + } + + static String computeKey(Device d) { + StringBuilder bld = new StringBuilder(d.getEntityClass().getName()); + bld.append("::"); + EnumSet<DeviceField> keyFields = d.getEntityClass().getKeyFields(); + if (keyFields.contains(DeviceField.MAC)) { + bld.append(d.getMACAddressString()); + bld.append("::"); + } + if (keyFields.contains(DeviceField.VLAN)) { + if (d.getVlanId() != null) + bld.append(Arrays.toString(d.getVlanId())); + bld.append("::"); + } + if (keyFields.contains(DeviceField.SWITCH) || + keyFields.contains(DeviceField.PORT) ) { + if (d.getAttachmentPoints(true) != null) + bld.append(Arrays.toString(d.getAttachmentPoints(true))); + bld.append("::"); + } + if (keyFields.contains(DeviceField.IPv4)) { + if (d.getIPv4Addresses() != null) + bld.append(Arrays.toString(d.getIPv4Addresses())); + bld.append("::"); + } + if (keyFields.contains(DeviceField.IPv6)) { + if (d.getIPv6Addresses() != null) + bld.append(Arrays.toString(d.getIPv6Addresses())); + bld.append("::"); + } + return bld.toString(); + } + + public String getKey() { + return key; + } + public void setKey(String key) { + this.key = key; + } + public List<SyncEntity> getEntities() { + return entities; + } + public void setEntities(List<SyncEntity> entities) { + if (entities == null) { + this.entities = null; + } else { + List<SyncEntity> tmp = new ArrayList<SyncEntity>(entities); + Collections.sort(tmp); + this.entities = tmp; + } + } + + @Override + public String toString() { + return key; + } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java index e373018bff2bc02a02a41384ae58daadaf45cb0b..3917f493b0788f409babae395de503cba71f0e6a 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java @@ -64,7 +64,7 @@ public class DeviceUniqueIndex extends DeviceIndex { public boolean updateIndex(Device device, Long deviceKey) { for (Entity e : device.entities) { IndexedEntity ie = new IndexedEntity(keyFields, e); - if (!ie.hasNonNullKeys()) continue; + if (!ie.hasNonZeroOrNonNullKeys()) continue; Long ret = index.putIfAbsent(ie, deviceKey); if (ret != null && !ret.equals(deviceKey)) { @@ -80,7 +80,7 @@ public class DeviceUniqueIndex extends DeviceIndex { @Override public boolean updateIndex(Entity entity, Long deviceKey) { IndexedEntity ie = new IndexedEntity(keyFields, entity); - if (!ie.hasNonNullKeys()) return false; + if (!ie.hasNonZeroOrNonNullKeys()) return false; index.put(ie, deviceKey); return true; } @@ -113,5 +113,4 @@ public class DeviceUniqueIndex extends DeviceIndex { return null; return deviceKey; } - -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java index 79c0199feae753336072b497e609ae891e13f81b..d1d24be6f19cfd8386a414827ab5bf878b6e1c2b 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java @@ -19,7 +19,10 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.Date; +import javax.annotation.Nonnull; + import net.floodlightcontroller.core.web.serializers.IPv4Serializer; +import net.floodlightcontroller.core.web.serializers.IPv6Serializer; import net.floodlightcontroller.core.web.serializers.DPIDSerializer; import net.floodlightcontroller.core.web.serializers.OFPortSerializer; import net.floodlightcontroller.core.web.serializers.VlanVidSerializer; @@ -30,6 +33,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -55,17 +59,25 @@ public class Entity implements Comparable<Entity> { */ protected static int ACTIVITY_TIMEOUT = 30000; + protected static final Date NO_DATE = new Date(0); /* Jan 1st 1970 00:00:00 */ + /** * The MAC address associated with this entity */ protected MacAddress macAddress; /** - * The IP address associated with this entity, or null if no IP learned + * The IPv4 address associated with this entity, or null if no IP learned * from the network observation associated with this entity */ protected IPv4Address ipv4Address; + /** + * The IPv6 address associated with this entity, or null if no IP learned + * from the network observation associated with this entity + */ + protected IPv6Address ipv6Address; + /** * The VLAN tag on this entity, or null if untagged */ @@ -108,15 +120,37 @@ public class Entity implements Comparable<Entity> { * @param macAddress * @param vlan * @param ipv4Address + * @param ipv6Address * @param switchDPID * @param switchPort * @param lastSeenTimestamp */ - public Entity(MacAddress macAddress, VlanVid vlan, - IPv4Address ipv4Address, DatapathId switchDPID, OFPort switchPort, - Date lastSeenTimestamp) { + public Entity(@Nonnull MacAddress macAddress, VlanVid vlan, @Nonnull IPv4Address ipv4Address, + @Nonnull IPv6Address ipv6Address, @Nonnull DatapathId switchDPID, @Nonnull OFPort switchPort, + @Nonnull Date lastSeenTimestamp) { + if (macAddress == null) { + throw new IllegalArgumentException("MAC address cannot be null. Try MacAddress.NONE if intention is 'no MAC'"); + } + if (ipv4Address == null) { + throw new IllegalArgumentException("IPv4 address cannot be null. Try IPv4Address.NONE if intention is 'no IPv4'"); + } + if (ipv6Address == null) { + throw new IllegalArgumentException("IPv6 address cannot be null. Try IPv6Address.NONE if intention is 'no IPv6'"); + } + /* VLAN can be null for 'don't care' in query searches */ + if (switchDPID == null) { + throw new IllegalArgumentException("Switch DPID cannot be null. Try DatapathId.NONE if intention is 'no DPID'"); + } + if (switchPort == null) { + throw new IllegalArgumentException("Switch port cannot be null. Try OFPort.ZERO if intention is 'no port'"); + } + if (lastSeenTimestamp == null) { + throw new IllegalArgumentException("Last seen time stamp cannot be null. Try Entity.NO_DATE if intention is 'no time'"); + } + this.macAddress = macAddress; this.ipv4Address = ipv4Address; + this.ipv6Address = ipv6Address; this.vlan = vlan; this.switchDPID = switchDPID; this.switchPort = switchPort; @@ -138,6 +172,11 @@ public class Entity implements Comparable<Entity> { return ipv4Address; } + @JsonSerialize(using=IPv6Serializer.class) + public IPv6Address getIpv6Address() { + return ipv6Address; + } + @JsonSerialize(using=VlanVidSerializer.class) public VlanVid getVlan() { return vlan; @@ -169,7 +208,8 @@ public class Entity implements Comparable<Entity> { * @see {@link Entity#activeSince} */ public void setLastSeenTimestamp(Date lastSeenTimestamp) { - if (activeSince == null || (activeSince.getTime() + ACTIVITY_TIMEOUT) < lastSeenTimestamp.getTime()) + if (activeSince.equals(Entity.NO_DATE) || + (activeSince.getTime() + ACTIVITY_TIMEOUT) < lastSeenTimestamp.getTime()) this.activeSince = lastSeenTimestamp; this.lastSeenTimestamp = lastSeenTimestamp; } @@ -188,6 +228,8 @@ public class Entity implements Comparable<Entity> { int result = 1; result = prime * result + ((ipv4Address == null) ? 0 : ipv4Address.hashCode()); + result = prime * result + + ((ipv6Address == null) ? 0 : ipv6Address.hashCode()); result = prime * result + ((macAddress == null) ? 0 : macAddress.hashCode()); result = prime * result @@ -207,11 +249,18 @@ public class Entity implements Comparable<Entity> { if (getClass() != obj.getClass()) return false; Entity other = (Entity) obj; + if (hashCode() != obj.hashCode()) + return false; if (ipv4Address == null) { if (other.ipv4Address != null) return false; } else if (!ipv4Address.equals(other.ipv4Address)) return false; + if (ipv6Address == null) { + if (other.ipv6Address != null) + return false; + } else if (!ipv6Address.equals(other.ipv6Address)) + return false; if (macAddress == null) { if (other.macAddress != null) return false; @@ -235,8 +284,6 @@ public class Entity implements Comparable<Entity> { return true; } - - @Override public String toString() { StringBuilder builder = new StringBuilder(); @@ -252,6 +299,12 @@ public class Entity implements Comparable<Entity> { } else { builder.append("null"); } + builder.append(", ipv6Address="); + if (ipv4Address != null) { + builder.append(ipv6Address.toString()); + } else { + builder.append("null"); + } builder.append(", vlan="); if (vlan != null) { builder.append(vlan.getVlan()); @@ -272,13 +325,14 @@ public class Entity implements Comparable<Entity> { } builder.append(", lastSeenTimestamp="); if (lastSeenTimestamp != null) { - builder.append(lastSeenTimestamp == null? "null" : lastSeenTimestamp.getTime()); + builder.append(lastSeenTimestamp == null? "null" : lastSeenTimestamp.toString()); + } else { builder.append("null"); } builder.append(", activeSince="); if (activeSince != null) { - builder.append(activeSince == null? "null" : activeSince.getTime()); + builder.append(activeSince == null? "null" : activeSince.toString()); } else { builder.append("null"); } @@ -315,6 +369,14 @@ public class Entity implements Comparable<Entity> { else r = ipv4Address.compareTo(o.ipv4Address); if (r != 0) return r; + + if (ipv6Address == null) + r = o.ipv6Address == null ? 0 : -1; + else if (o.ipv6Address == null) + r = 1; + else + r = ipv6Address.compareTo(o.ipv6Address); + if (r != 0) return r; if (vlan == null) r = o.vlan == null ? 0 : -1; @@ -326,5 +388,4 @@ public class Entity implements Comparable<Entity> { return 0; } - -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java index 6fb1dda1305e4e35e86cf95131a2edf17f843895..503b268529c4bfecdb2ce0b6833ed46c3a7dc9cd 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/IndexedEntity.java @@ -18,6 +18,10 @@ package net.floodlightcontroller.devicemanager.internal; import java.util.EnumSet; +import org.projectfloodlight.openflow.types.DatapathId; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.OFPort; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,24 +55,27 @@ public class IndexedEntity { } /** - * Check whether this entity has non-null values in any of its key fields + * Check whether this entity has non-'zero' values in any of its key fields * @return true if any key fields have a non-null value */ - public boolean hasNonNullKeys() { + public boolean hasNonZeroOrNonNullKeys() { for (DeviceField f : keyFields) { switch (f) { - case MAC: + case MAC: /* We assume operation over Ethernet, thus all devices must have a MAC */ return true; - case IPV4: - if (entity.ipv4Address != null) return true; + case IPv4: + if (!entity.ipv4Address.equals(IPv4Address.NONE)) return true; + break; + case IPv6: + if (!entity.ipv6Address.equals(IPv6Address.NONE)) return true; break; case SWITCH: - if (entity.switchDPID != null) return true; + if (!entity.switchDPID.equals(DatapathId.NONE)) return true; break; case PORT: - if (entity.switchPort != null) return true; + if (!entity.switchPort.equals(OFPort.ZERO)) return true; break; - case VLAN: + case VLAN: /* VLAN can still be null, meaning 'don't care'. VlanVid.ZERO means 'untagged' */ if (entity.vlan != null) return true; break; } @@ -92,12 +99,18 @@ public class IndexedEntity { + (int) (entity.macAddress.getLong() ^ (entity.macAddress.getLong() >>> 32)); break; - case IPV4: + case IPv4: hashCode = prime * hashCode + ((entity.ipv4Address == null) ? 0 : entity.ipv4Address.hashCode()); break; + case IPv6: + hashCode = prime * hashCode + + ((entity.ipv6Address == null) + ? 0 + : entity.ipv6Address.hashCode()); + break; case SWITCH: hashCode = prime * hashCode + ((entity.switchDPID == null) @@ -137,11 +150,16 @@ public class IndexedEntity { if (!entity.macAddress.equals(other.entity.macAddress)) return false; break; - case IPV4: + case IPv4: if (entity.ipv4Address == null) { if (other.entity.ipv4Address != null) return false; } else if (!entity.ipv4Address.equals(other.entity.ipv4Address)) return false; break; + case IPv6: + if (entity.ipv6Address == null) { + if (other.entity.ipv6Address != null) return false; + } else if (!entity.ipv6Address.equals(other.entity.ipv6Address)) return false; + break; case SWITCH: if (entity.switchDPID == null) { if (other.entity.switchDPID != null) return false; @@ -158,10 +176,7 @@ public class IndexedEntity { } else if (!entity.vlan.equals(other.entity.vlan)) return false; break; } - } - + } return true; } - - -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java b/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java index 512e4912babe760a3dbf44049465fd4424f0e96e..595650f45ad385c76c4f5cf49148102a877c9d25 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/web/AbstractDeviceResource.java @@ -27,6 +27,7 @@ import net.floodlightcontroller.util.FilterIterator; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -46,6 +47,8 @@ public abstract class AbstractDeviceResource extends ServerResource { public static final String IPV4_ERROR = "Invalid IPv4 address: must be in dotted decimal format, " + "234.0.59.1"; + public static final String IPV6_ERROR = + "Invalid IPv6 address: must be a valid IPv6 format."; public static final String DPID_ERROR = "Invalid Switch DPID: must be a 64-bit quantity, expressed in " + "hex as AA:BB:CC:DD:EE:FF:00:11"; @@ -57,16 +60,18 @@ public abstract class AbstractDeviceResource extends ServerResource { (IDeviceService)getContext().getAttributes(). get(IDeviceService.class.getCanonicalName()); - MacAddress macAddress = null; - VlanVid vlan = null; - IPv4Address ipv4Address = null; - DatapathId switchDPID = null; - OFPort switchPort = null; + MacAddress macAddress = MacAddress.NONE; + VlanVid vlan = null; /* must be null for don't care */ + IPv4Address ipv4Address = IPv4Address.NONE; + IPv6Address ipv6Address = IPv6Address.NONE; + DatapathId switchDPID = DatapathId.NONE; + OFPort switchPort = OFPort.ZERO; Form form = getQuery(); String macAddrStr = form.getFirstValue("mac", true); String vlanStr = form.getFirstValue("vlan", true); String ipv4Str = form.getFirstValue("ipv4", true); + String ipv6Str = form.getFirstValue("ipv6", true); String dpid = form.getFirstValue("dpid", true); String port = form.getFirstValue("port", true); @@ -98,6 +103,14 @@ public abstract class AbstractDeviceResource extends ServerResource { return null; } } + if (ipv6Str != null) { + try { + ipv6Address = IPv6Address.of(ipv6Str); + } catch (Exception e) { + setStatus(Status.CLIENT_ERROR_BAD_REQUEST, IPV6_ERROR); + return null; + } + } if (dpid != null) { try { switchDPID = DatapathId.of(dpid); @@ -124,6 +137,7 @@ public abstract class AbstractDeviceResource extends ServerResource { deviceManager.queryDevices(macAddress, vlan, ipv4Address, + ipv6Address, switchDPID, switchPort); @@ -133,6 +147,8 @@ public abstract class AbstractDeviceResource extends ServerResource { form.getFirstValue("vlan__startswith", true); final String ipv4StartsWith = form.getFirstValue("ipv4__startswith", true); + final String ipv6StartsWith = + form.getFirstValue("ipv6__startswith", true); final String dpidStartsWith = form.getFirstValue("dpid__startswith", true); final String portStartsWith = @@ -169,6 +185,19 @@ public abstract class AbstractDeviceResource extends ServerResource { } if (!match) return false; } + if (ipv6StartsWith != null) { + boolean match = false; + for (IPv6Address v : value.getIPv6Addresses()) { + String str; + if (v != null && + (str = v.toString()) != null && + str.startsWith(ipv6StartsWith)) { + match = true; + break; + } + } + if (!match) return false; + } if (dpidStartsWith != null) { boolean match = false; for (SwitchPort v : value.getAttachmentPoints(true)) { diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java index e45e438056ea2b6c55e982fbaff106fb57efa9b8..7dca0ea07de36694298ab99abf8a7bdc28e9d5f2 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java @@ -20,6 +20,7 @@ package net.floodlightcontroller.devicemanager.web; import java.io.IOException; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.VlanVid; import net.floodlightcontroller.devicemanager.SwitchPort; @@ -51,6 +52,11 @@ public class DeviceSerializer extends JsonSerializer<Device> { for (IPv4Address ip : device.getIPv4Addresses()) jGen.writeString(ip.toString()); jGen.writeEndArray(); + + jGen.writeArrayFieldStart("ipv6"); + for (IPv6Address ip : device.getIPv6Addresses()) + jGen.writeString(ip.toString()); + jGen.writeEndArray(); jGen.writeArrayFieldStart("vlan"); for (VlanVid vlan : device.getVlanId()) diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index f347ef87d18ccb33d017d7e46785fa2f9ae926d8..7f07b1a595a9bc7bc6098be628133b81b2adb0f8 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -42,6 +42,7 @@ import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.IPv6; import net.floodlightcontroller.packet.TCP; import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.routing.ForwardingBase; @@ -60,6 +61,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBufferId; @@ -181,6 +183,8 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) { DatapathId dstSwDpid = dstDap.getSwitchDPID(); DatapathId dstIsland = topologyService.getL2DomainId(dstSwDpid); + log.trace("Source Island: {}, Destination Island: {}", srcIsland, dstIsland); + log.trace("Source Device: {}, Destination Device: {}", srcDevice, dstDevice); if ((dstIsland != null) && dstIsland.equals(srcIsland)) { on_same_island = true; if (sw.getId().equals(dstSwDpid) && inPort.equals(dstDap.getPort())) { @@ -337,6 +341,38 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { } } else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */ mb.setExact(MatchField.ETH_TYPE, EthType.ARP); + } else if (eth.getEtherType() == EthType.IPv6) { + IPv6 ip = (IPv6) eth.getPayload(); + IPv6Address srcIp = ip.getSourceAddress(); + IPv6Address dstIp = ip.getDestinationAddress(); + + if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv6) + .setExact(MatchField.IPV6_SRC, srcIp) + .setExact(MatchField.IPV6_DST, dstIp); + } + + if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) { + /* + * Take care of the ethertype if not included earlier, + * since it's a prerequisite for transport ports. + */ + if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) { + mb.setExact(MatchField.ETH_TYPE, EthType.IPv6); + } + + if (ip.getNextHeader().equals(IpProtocol.TCP)) { + TCP tcp = (TCP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP) + .setExact(MatchField.TCP_SRC, tcp.getSourcePort()) + .setExact(MatchField.TCP_DST, tcp.getDestinationPort()); + } else if (ip.getNextHeader().equals(IpProtocol.UDP)) { + UDP udp = (UDP) ip.getPayload(); + mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP) + .setExact(MatchField.UDP_SRC, udp.getSourcePort()) + .setExact(MatchField.UDP_DST, udp.getDestinationPort()); + } + } } return mb.build(); } @@ -470,6 +506,13 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { } else { log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY); } + tmp = configParameters.get("set-send-flow-rem-flag"); + if (tmp != null) { + FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp); + log.info("Default flags will be set to SEND_FLOW_REM."); + } else { + log.info("Default flags will be empty."); + } tmp = configParameters.get("match"); if (tmp != null) { tmp = tmp.toLowerCase(); diff --git a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java index bbc1a01042b4e52cc6c4c2e0ea4c36b5913a07c5..b36262bb6b8e86468b7d0bd8156d0e3ccbd2fab2 100644 --- a/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java +++ b/src/main/java/net/floodlightcontroller/learningswitch/LearningSwitch.java @@ -53,6 +53,7 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService; import net.floodlightcontroller.debugcounter.IDebugCounterService.MetaData; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.restserver.IRestApiService; +import net.floodlightcontroller.util.OFMessageUtils; import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.OFFlowRemoved; @@ -354,27 +355,8 @@ implements IFloodlightModule, ILearningSwitchService, IOFMessageListener { * @param egressPort The switchport to output the PacketOut. */ private void writePacketOutForPacketIn(IOFSwitch sw, OFPacketIn packetInMessage, OFPort egressPort) { - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); - - // Set buffer_id, in_port, actions_len - pob.setBufferId(packetInMessage.getBufferId()); - pob.setInPort(packetInMessage.getVersion().compareTo(OFVersion.OF_12) < 0 ? packetInMessage.getInPort() : packetInMessage.getMatch().get(MatchField.IN_PORT)); - - // set actions - List<OFAction> actions = new ArrayList<OFAction>(1); - actions.add(sw.getOFFactory().actions().buildOutput().setPort(egressPort).setMaxLen(0xffFFffFF).build()); - pob.setActions(actions); - - // set data - only if buffer_id == -1 - if (packetInMessage.getBufferId() == OFBufferId.NO_BUFFER) { - byte[] packetData = packetInMessage.getData(); - pob.setData(packetData); - } - - // and write it out + OFMessageUtils.writePacketOutForPacketIn(sw, packetInMessage, egressPort); counterPacketOut.increment(); - sw.write(pob.build()); - } protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) { diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java index 2bb53727917ffba3b4138bf3d06a9e56b79db1f2..df179c5952c4c7b738ed17065afe98549c94be87 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java @@ -25,6 +25,7 @@ import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; +import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.routing.Link; import net.floodlightcontroller.topology.NodePortTuple; @@ -62,7 +63,7 @@ public interface ILinkDiscoveryService extends IFloodlightService { * to switchport (sw, port). PacketOut does not contain actions. * PacketOut length includes the minimum length and data length. */ - public OFPacketOut generateLLDPMessage(DatapathId sw, OFPort port, + public OFPacketOut generateLLDPMessage(IOFSwitch iofSwitch, OFPort port, boolean isStandard, boolean isReverse); diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index ba3eaa081f7576b81e227c688c39f06fce469ff3..4f41f919b0e3f6dda32c12facc0a168a9bc8a55b 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -288,15 +288,14 @@ IFloodlightModule, IInfoProvider { //********************* @Override - public OFPacketOut generateLLDPMessage(DatapathId sw, OFPort port, + public OFPacketOut generateLLDPMessage(IOFSwitch iofSwitch, OFPort port, boolean isStandard, boolean isReverse) { - - IOFSwitch iofSwitch = switchService.getSwitch(sw); + OFPortDesc ofpPort = iofSwitch.getPort(port); if (log.isTraceEnabled()) { log.trace("Sending LLDP packet out of swich: {}, port: {}", - sw.toString(), port); + iofSwitch.getId().toString(), port); } // using "nearest customer bridge" MAC address for broadest possible @@ -320,7 +319,7 @@ IFloodlightModule, IInfoProvider { ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray); ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2); - DatapathId dpid = sw; + DatapathId dpid = iofSwitch.getId(); dpidBB.putLong(dpid.getLong()); // set the chassis id's value to last 6 bytes of dpid System.arraycopy(dpidArray, 2, chassisId, 1, 6); @@ -344,7 +343,7 @@ IFloodlightModule, IInfoProvider { portBB.putShort(port.getShortPortNumber()); if (log.isTraceEnabled()) { log.trace("Sending LLDP out of interface: {}/{}", - sw.toString(), port); + iofSwitch.getId().toString(), port); } LLDP lldp = new LLDP(); @@ -385,7 +384,7 @@ IFloodlightModule, IInfoProvider { // serialize and wrap in a packet out byte[] data = ethernet.serialize(); - OFPacketOut.Builder pob = switchService.getSwitch(sw).getOFFactory().buildPacketOut(); + OFPacketOut.Builder pob = iofSwitch.getOFFactory().buildPacketOut(); pob.setBufferId(OFBufferId.NO_BUFFER); pob.setInPort(OFPort.ANY); @@ -1160,13 +1159,15 @@ IFloodlightModule, IInfoProvider { return; IOFSwitch iofSwitch = switchService.getSwitch(sw); + if (iofSwitch == null) //fix dereference violations in case race conditions + return; OFPortDesc ofpPort = iofSwitch.getPort(port); if (log.isTraceEnabled()) { log.trace("Sending LLDP packet out of swich: {}, port: {}", sw.toString(), port.getPortNumber()); } - OFPacketOut po = generateLLDPMessage(sw, port, isStandard, isReverse); + OFPacketOut po = generateLLDPMessage(iofSwitch, port, isStandard, isReverse); OFPacketOut.Builder pob = po.createBuilder(); // Add actions @@ -1687,6 +1688,8 @@ IFloodlightModule, IInfoProvider { @Override public void switchActivated(DatapathId switchId) { IOFSwitch sw = switchService.getSwitch(switchId); + if (sw == null) //fix dereference violation in case race conditions + return; if (sw.getEnabledPortNumbers() != null) { for (OFPort p : sw.getEnabledPortNumbers()) { processNewPort(sw.getId(), p); diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java index d9c45265cab567837c5dcd1ebc9c1f6d4e48ee69..e691cffae07c9d734b2dea01174d11b2f577141d 100644 --- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java +++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java @@ -190,11 +190,10 @@ public class LoadBalancer implements IFloodlightModule, // retrieve arp to determine target IP address ARP arpRequest = (ARP) eth.getPayload(); - int targetProtocolAddress = IPv4.toIPv4Address(arpRequest - .getTargetProtocolAddress()); + IPv4Address targetProtocolAddress = arpRequest.getTargetProtocolAddress(); - if (vipIpToId.containsKey(targetProtocolAddress)) { - String vipId = vipIpToId.get(targetProtocolAddress); + if (vipIpToId.containsKey(targetProtocolAddress.getInt())) { + String vipId = vipIpToId.get(targetProtocolAddress.getInt()); vipProxyArpReply(sw, pi, cntx, vipId); return Command.STOP; } @@ -227,9 +226,15 @@ public class LoadBalancer implements IFloodlightModule, } LBVip vip = vips.get(vipIpToId.get(destIpAddress)); + if (vip == null) // fix deference violations + return Command.CONTINUE; LBPool pool = pools.get(vip.pickPool(client)); + if (pool == null) // fix deference violations + return Command.CONTINUE; LBMember member = members.get(pool.pickMember(client)); - + if(member == null) //fix deference violations + return Command.CONTINUE; + // for chosen member, check device manager and find and push routes, in both directions pushBidirectionalVipRoutes(sw, pi, cntx, client, member); @@ -265,11 +270,10 @@ public class LoadBalancer implements IFloodlightModule, ARP arpRequest = (ARP) eth.getPayload(); // have to do proxy arp reply since at this point we cannot determine the requesting application type - byte[] vipProxyMacBytes = vips.get(vipId).proxyMac.getBytes(); // generate proxy ARP reply IPacket arpReply = new Ethernet() - .setSourceMACAddress(vipProxyMacBytes) + .setSourceMACAddress(vips.get(vipId).proxyMac) .setDestinationMACAddress(eth.getSourceMACAddress()) .setEtherType(EthType.ARP) .setVlanID(eth.getVlanID()) @@ -281,13 +285,10 @@ public class LoadBalancer implements IFloodlightModule, .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) - .setSenderHardwareAddress(vipProxyMacBytes) - .setSenderProtocolAddress( - arpRequest.getTargetProtocolAddress()) - .setTargetHardwareAddress( - eth.getSourceMACAddress().getBytes()) - .setTargetProtocolAddress( - arpRequest.getSenderProtocolAddress())); + .setSenderHardwareAddress(vips.get(vipId).proxyMac) + .setSenderProtocolAddress(arpRequest.getTargetProtocolAddress()) + .setTargetHardwareAddress(eth.getSourceMACAddress()) + .setTargetProtocolAddress(arpRequest.getSenderProtocolAddress())); // push ARP reply out pushPacket(arpReply, sw, OFBufferId.NO_BUFFER, OFPort.ANY, (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)), cntx, true); @@ -667,6 +668,8 @@ public class LoadBalancer implements IFloodlightModule, LBPool pool; if (pools != null) { pool = pools.get(poolId); + if (pool == null) // fix deference violations + return -1; if (pool.vipId != null) vips.get(pool.vipId).pools.remove(poolId); pools.remove(poolId); diff --git a/src/main/java/net/floodlightcontroller/packet/ARP.java b/src/main/java/net/floodlightcontroller/packet/ARP.java index bf3f49385f440f8ed07571c38d341b13ab19e080..48bcd13c9ec1a3436771a5e6abc4ffd4ac7eec77 100644 --- a/src/main/java/net/floodlightcontroller/packet/ARP.java +++ b/src/main/java/net/floodlightcontroller/packet/ARP.java @@ -1,327 +1,333 @@ /** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ + * Copyright 2011, Big Switch Networks, Inc. + * Originally created by David Erickson, Stanford University + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ package net.floodlightcontroller.packet; import java.nio.ByteBuffer; -import java.util.Arrays; + +import org.projectfloodlight.openflow.types.ArpOpcode; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.MacAddress; /** * * @author David Erickson (daviderickson@cs.stanford.edu) */ public class ARP extends BasePacket { - public static short HW_TYPE_ETHERNET = 0x1; - - public static short PROTO_TYPE_IP = 0x800; - - public static short OP_REQUEST = 0x1; - public static short OP_REPLY = 0x2; - public static short OP_RARP_REQUEST = 0x3; - public static short OP_RARP_REPLY = 0x4; - - protected short hardwareType; - protected short protocolType; - protected byte hardwareAddressLength; - protected byte protocolAddressLength; - protected short opCode; - protected byte[] senderHardwareAddress; - protected byte[] senderProtocolAddress; - protected byte[] targetHardwareAddress; - protected byte[] targetProtocolAddress; - - /** - * @return the hardwareType - */ - public short getHardwareType() { - return hardwareType; - } - - /** - * @param hardwareType the hardwareType to set - */ - public ARP setHardwareType(short hardwareType) { - this.hardwareType = hardwareType; - return this; - } - - /** - * @return the protocolType - */ - public short getProtocolType() { - return protocolType; - } - - /** - * @param protocolType the protocolType to set - */ - public ARP setProtocolType(short protocolType) { - this.protocolType = protocolType; - return this; - } - - /** - * @return the hardwareAddressLength - */ - public byte getHardwareAddressLength() { - return hardwareAddressLength; - } - - /** - * @param hardwareAddressLength the hardwareAddressLength to set - */ - public ARP setHardwareAddressLength(byte hardwareAddressLength) { - this.hardwareAddressLength = hardwareAddressLength; - return this; - } - - /** - * @return the protocolAddressLength - */ - public byte getProtocolAddressLength() { - return protocolAddressLength; - } - - /** - * @param protocolAddressLength the protocolAddressLength to set - */ - public ARP setProtocolAddressLength(byte protocolAddressLength) { - this.protocolAddressLength = protocolAddressLength; - return this; - } - - /** - * @return the opCode - */ - public short getOpCode() { - return opCode; - } - - /** - * @param opCode the opCode to set - */ - public ARP setOpCode(short opCode) { - this.opCode = opCode; - return this; - } - - /** - * @return the senderHardwareAddress - */ - public byte[] getSenderHardwareAddress() { - return senderHardwareAddress; - } - - /** - * @param senderHardwareAddress the senderHardwareAddress to set - */ - public ARP setSenderHardwareAddress(byte[] senderHardwareAddress) { - this.senderHardwareAddress = senderHardwareAddress; - return this; - } - - /** - * @return the senderProtocolAddress - */ - public byte[] getSenderProtocolAddress() { - return senderProtocolAddress; - } - - /** - * @param senderProtocolAddress the senderProtocolAddress to set - */ - public ARP setSenderProtocolAddress(byte[] senderProtocolAddress) { - this.senderProtocolAddress = senderProtocolAddress; - return this; - } - - public ARP setSenderProtocolAddress(int address) { - this.senderProtocolAddress = ByteBuffer.allocate(4).putInt(address).array(); - return this; - } - - /** - * @return the targetHardwareAddress - */ - public byte[] getTargetHardwareAddress() { - return targetHardwareAddress; - } - - /** - * @param targetHardwareAddress the targetHardwareAddress to set - */ - public ARP setTargetHardwareAddress(byte[] targetHardwareAddress) { - this.targetHardwareAddress = targetHardwareAddress; - return this; - } - - /** - * @return the targetProtocolAddress - */ - public byte[] getTargetProtocolAddress() { - return targetProtocolAddress; - } - - /** - * @return True if gratuitous ARP (SPA = TPA), false otherwise - */ - public boolean isGratuitous() { - assert(senderProtocolAddress.length == targetProtocolAddress.length); - - int indx = 0; - while (indx < senderProtocolAddress.length) { - if (senderProtocolAddress[indx] != targetProtocolAddress[indx]) { - return false; - } - indx++; - } - - return true; - } - - /** - * @param targetProtocolAddress the targetProtocolAddress to set - */ - public ARP setTargetProtocolAddress(byte[] targetProtocolAddress) { - this.targetProtocolAddress = targetProtocolAddress; - return this; - } - - public ARP setTargetProtocolAddress(int address) { - this.targetProtocolAddress = ByteBuffer.allocate(4).putInt(address).array(); - return this; - } - - @Override - public byte[] serialize() { - int length = 8 + (2 * (0xff & this.hardwareAddressLength)) - + (2 * (0xff & this.protocolAddressLength)); - byte[] data = new byte[length]; - ByteBuffer bb = ByteBuffer.wrap(data); - bb.putShort(this.hardwareType); - bb.putShort(this.protocolType); - bb.put(this.hardwareAddressLength); - bb.put(this.protocolAddressLength); - bb.putShort(this.opCode); - bb.put(this.senderHardwareAddress, 0, 0xff & this.hardwareAddressLength); - bb.put(this.senderProtocolAddress, 0, 0xff & this.protocolAddressLength); - bb.put(this.targetHardwareAddress, 0, 0xff & this.hardwareAddressLength); - bb.put(this.targetProtocolAddress, 0, 0xff & this.protocolAddressLength); - return data; - } - - @Override - public IPacket deserialize(byte[] data, int offset, int length) - throws PacketParsingException { - ByteBuffer bb = ByteBuffer.wrap(data, offset, length); - this.hardwareType = bb.getShort(); - this.protocolType = bb.getShort(); - this.hardwareAddressLength = bb.get(); - this.protocolAddressLength = bb.get(); - if (this.hardwareAddressLength != 6) { - String msg = "Incorrect ARP hardware address length: " + - hardwareAddressLength; - throw new PacketParsingException(msg); - } - if (this.protocolAddressLength != 4) { - String msg = "Incorrect ARP protocol address length: " + - protocolAddressLength; - throw new PacketParsingException(msg); - } - this.opCode = bb.getShort(); - this.senderHardwareAddress = new byte[0xff & this.hardwareAddressLength]; - bb.get(this.senderHardwareAddress, 0, this.senderHardwareAddress.length); - this.senderProtocolAddress = new byte[0xff & this.protocolAddressLength]; - bb.get(this.senderProtocolAddress, 0, this.senderProtocolAddress.length); - this.targetHardwareAddress = new byte[0xff & this.hardwareAddressLength]; - bb.get(this.targetHardwareAddress, 0, this.targetHardwareAddress.length); - this.targetProtocolAddress = new byte[0xff & this.protocolAddressLength]; - bb.get(this.targetProtocolAddress, 0, this.targetProtocolAddress.length); - return this; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 13121; - int result = super.hashCode(); - result = prime * result + hardwareAddressLength; - result = prime * result + hardwareType; - result = prime * result + opCode; - result = prime * result + protocolAddressLength; - result = prime * result + protocolType; - result = prime * result + Arrays.hashCode(senderHardwareAddress); - result = prime * result + Arrays.hashCode(senderProtocolAddress); - result = prime * result + Arrays.hashCode(targetHardwareAddress); - result = prime * result + Arrays.hashCode(targetProtocolAddress); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (!(obj instanceof ARP)) - return false; - ARP other = (ARP) obj; - if (hardwareAddressLength != other.hardwareAddressLength) - return false; - if (hardwareType != other.hardwareType) - return false; - if (opCode != other.opCode) - return false; - if (protocolAddressLength != other.protocolAddressLength) - return false; - if (protocolType != other.protocolType) - return false; - if (!Arrays.equals(senderHardwareAddress, other.senderHardwareAddress)) - return false; - if (!Arrays.equals(senderProtocolAddress, other.senderProtocolAddress)) - return false; - if (!Arrays.equals(targetHardwareAddress, other.targetHardwareAddress)) - return false; - if (!Arrays.equals(targetProtocolAddress, other.targetProtocolAddress)) - return false; - return true; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "ARP [hardwareType=" + hardwareType + ", protocolType=" - + protocolType + ", hardwareAddressLength=" - + hardwareAddressLength + ", protocolAddressLength=" - + protocolAddressLength + ", opCode=" + opCode - + ", senderHardwareAddress=" - + Arrays.toString(senderHardwareAddress) - + ", senderProtocolAddress=" - + Arrays.toString(senderProtocolAddress) - + ", targetHardwareAddress=" - + Arrays.toString(targetHardwareAddress) - + ", targetProtocolAddress=" - + Arrays.toString(targetProtocolAddress) + "]"; - } + public static short HW_TYPE_ETHERNET = 0x1; + + public static short PROTO_TYPE_IP = 0x800; + + public static ArpOpcode OP_REQUEST = ArpOpcode.REQUEST; + public static ArpOpcode OP_REPLY = ArpOpcode.REPLY; + public static ArpOpcode OP_RARP_REQUEST = ArpOpcode.REQUEST_REVERSE; + public static ArpOpcode OP_RARP_REPLY = ArpOpcode.REPLY_REVERSE; + + protected short hardwareType; + protected short protocolType; + protected byte hardwareAddressLength; + protected byte protocolAddressLength; + protected ArpOpcode opCode; + protected MacAddress senderHardwareAddress; + protected IPv4Address senderProtocolAddress; + protected MacAddress targetHardwareAddress; + protected IPv4Address targetProtocolAddress; + + /** + * @return the hardwareType + */ + public short getHardwareType() { + return hardwareType; + } + + /** + * @param hardwareType the hardwareType to set + */ + public ARP setHardwareType(short hardwareType) { + this.hardwareType = hardwareType; + return this; + } + + /** + * @return the protocolType + */ + public short getProtocolType() { + return protocolType; + } + + /** + * @param protocolType the protocolType to set + */ + public ARP setProtocolType(short protocolType) { + this.protocolType = protocolType; + return this; + } + + /** + * @return the hardwareAddressLength + */ + public byte getHardwareAddressLength() { + return hardwareAddressLength; + } + + /** + * @param hardwareAddressLength the hardwareAddressLength to set + */ + public ARP setHardwareAddressLength(byte hardwareAddressLength) { + this.hardwareAddressLength = hardwareAddressLength; + return this; + } + + /** + * @return the protocolAddressLength + */ + public byte getProtocolAddressLength() { + return protocolAddressLength; + } + + /** + * @param protocolAddressLength the protocolAddressLength to set + */ + public ARP setProtocolAddressLength(byte protocolAddressLength) { + this.protocolAddressLength = protocolAddressLength; + return this; + } + + /** + * @return the opCode + */ + public ArpOpcode getOpCode() { + return opCode; + } + + /** + * @param opCode the opCode to set + */ + public ARP setOpCode(ArpOpcode opCode) { + this.opCode = opCode; + return this; + } + + /** + * @return the senderHardwareAddress + */ + public MacAddress getSenderHardwareAddress() { + return senderHardwareAddress; + } + + /** + * @param senderHardwareAddress the senderHardwareAddress to set + */ + public ARP setSenderHardwareAddress(MacAddress senderHardwareAddress) { + this.senderHardwareAddress = senderHardwareAddress; + return this; + } + + /** + * @return the senderProtocolAddress + */ + public IPv4Address getSenderProtocolAddress() { + return senderProtocolAddress; + } + + /** + * @param senderProtocolAddress the senderProtocolAddress to set + */ + public ARP setSenderProtocolAddress(IPv4Address senderProtocolAddress) { + this.senderProtocolAddress = senderProtocolAddress; + return this; + } + + /** + * @return the targetHardwareAddress + */ + public MacAddress getTargetHardwareAddress() { + return targetHardwareAddress; + } + + /** + * @param targetHardwareAddress the targetHardwareAddress to set + */ + public ARP setTargetHardwareAddress(MacAddress targetHardwareAddress) { + this.targetHardwareAddress = targetHardwareAddress; + return this; + } + + /** + * @return the targetProtocolAddress + */ + public IPv4Address getTargetProtocolAddress() { + return targetProtocolAddress; + } + + /** + * @return True if gratuitous ARP (SPA = TPA), false otherwise + */ + public boolean isGratuitous() { + assert(senderProtocolAddress.getLength() == targetProtocolAddress.getLength()); + + return senderProtocolAddress.equals(targetProtocolAddress); + } + + /** + * @param targetProtocolAddress the targetProtocolAddress to set + */ + public ARP setTargetProtocolAddress(IPv4Address targetProtocolAddress) { + this.targetProtocolAddress = targetProtocolAddress; + return this; + } + + @Override + public byte[] serialize() { + int length = 8 + (2 * (0xff & this.hardwareAddressLength)) + + (2 * (0xff & this.protocolAddressLength)); + byte[] data = new byte[length]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.putShort(this.hardwareType); + bb.putShort(this.protocolType); + bb.put(this.hardwareAddressLength); + bb.put(this.protocolAddressLength); + bb.putShort((short) this.opCode.getOpcode()); + bb.put(this.senderHardwareAddress.getBytes()); + bb.put(this.senderProtocolAddress.getBytes()); + bb.put(this.targetHardwareAddress.getBytes()); + bb.put(this.targetProtocolAddress.getBytes()); + return data; + } + + @Override + public IPacket deserialize(byte[] data, int offset, int length) + throws PacketParsingException { + ByteBuffer bb = ByteBuffer.wrap(data, offset, length); + this.hardwareType = bb.getShort(); + this.protocolType = bb.getShort(); + this.hardwareAddressLength = bb.get(); + this.protocolAddressLength = bb.get(); + if (this.hardwareAddressLength != 6) { + String msg = "Incorrect ARP hardware address length: " + + hardwareAddressLength; + throw new PacketParsingException(msg); + } + if (this.protocolAddressLength != 4) { + String msg = "Incorrect ARP protocol address length: " + + protocolAddressLength; + throw new PacketParsingException(msg); + } + this.opCode = ArpOpcode.of(bb.getShort()); + + byte[] tmpMac = new byte[0xff & this.hardwareAddressLength]; + byte[] tmpIp = new byte[0xff & this.protocolAddressLength]; + + bb.get(tmpMac, 0, this.hardwareAddressLength); + this.senderHardwareAddress = MacAddress.of(tmpMac); + bb.get(tmpIp, 0, this.protocolAddressLength); + this.senderProtocolAddress = IPv4Address.of(tmpIp); + + bb.get(tmpMac, 0, this.hardwareAddressLength); + this.targetHardwareAddress = MacAddress.of(tmpMac); + bb.get(tmpIp, 0, this.protocolAddressLength); + this.targetProtocolAddress = IPv4Address.of(tmpIp); + + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + hardwareAddressLength; + result = prime * result + hardwareType; + result = prime * result + ((opCode == null) ? 0 : opCode.hashCode()); + result = prime * result + protocolAddressLength; + result = prime * result + protocolType; + result = prime + * result + + ((senderHardwareAddress == null) ? 0 : senderHardwareAddress + .hashCode()); + result = prime + * result + + ((senderProtocolAddress == null) ? 0 : senderProtocolAddress + .hashCode()); + result = prime + * result + + ((targetHardwareAddress == null) ? 0 : targetHardwareAddress + .hashCode()); + result = prime + * result + + ((targetProtocolAddress == null) ? 0 : targetProtocolAddress + .hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + ARP other = (ARP) obj; + if (hardwareAddressLength != other.hardwareAddressLength) + return false; + if (hardwareType != other.hardwareType) + return false; + if (opCode == null) { + if (other.opCode != null) + return false; + } else if (!opCode.equals(other.opCode)) + return false; + if (protocolAddressLength != other.protocolAddressLength) + return false; + if (protocolType != other.protocolType) + return false; + if (senderHardwareAddress == null) { + if (other.senderHardwareAddress != null) + return false; + } else if (!senderHardwareAddress.equals(other.senderHardwareAddress)) + return false; + if (senderProtocolAddress == null) { + if (other.senderProtocolAddress != null) + return false; + } else if (!senderProtocolAddress.equals(other.senderProtocolAddress)) + return false; + if (targetHardwareAddress == null) { + if (other.targetHardwareAddress != null) + return false; + } else if (!targetHardwareAddress.equals(other.targetHardwareAddress)) + return false; + if (targetProtocolAddress == null) { + if (other.targetProtocolAddress != null) + return false; + } else if (!targetProtocolAddress.equals(other.targetProtocolAddress)) + return false; + return true; + } + + @Override + public String toString() { + return "ARP [hardwareType=" + hardwareType + ", protocolType=" + + protocolType + ", hardwareAddressLength=" + + hardwareAddressLength + ", protocolAddressLength=" + + protocolAddressLength + ", opCode=" + opCode + + ", senderHardwareAddress=" + senderHardwareAddress + + ", senderProtocolAddress=" + senderProtocolAddress + + ", targetHardwareAddress=" + targetHardwareAddress + + ", targetProtocolAddress=" + targetProtocolAddress + "]"; + } + } diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java index 71ef21907ceb462288c4e1634bb9c9d9af1475ad..b3ef406e69298fc785e975cb488fb3833de5d36b 100644 --- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java +++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -35,6 +35,7 @@ public class Ethernet extends BasePacket { public static final short TYPE_ARP = 0x0806; public static final short TYPE_RARP = (short) 0x8035; public static final short TYPE_IPv4 = 0x0800; + public static final short TYPE_IPv6 = (short) 0x86DD; public static final short TYPE_LLDP = (short) 0x88cc; public static final short TYPE_BSN = (short) 0x8942; public static final short VLAN_UNTAGGED = VlanVid.ZERO.getVlan(); // untagged vlan must be 0x0000 for loxi. We can use the convenient ZERO field @@ -46,6 +47,7 @@ public class Ethernet extends BasePacket { etherTypeClassMap.put(TYPE_ARP, ARP.class); etherTypeClassMap.put(TYPE_RARP, ARP.class); etherTypeClassMap.put(TYPE_IPv4, IPv4.class); + etherTypeClassMap.put(TYPE_IPv6, IPv6.class); etherTypeClassMap.put(TYPE_LLDP, LLDP.class); etherTypeClassMap.put(TYPE_BSN, BSN.class); } @@ -64,7 +66,7 @@ public class Ethernet extends BasePacket { super(); this.vlanID = VLAN_UNTAGGED; } - + /** * @return the destination MAC */ @@ -79,7 +81,7 @@ public class Ethernet extends BasePacket { this.destinationMACAddress = MacAddress.of(destinationMACAddress); return this; } - + /** * @param destinationMACAddress the destination MAC to set */ @@ -110,7 +112,7 @@ public class Ethernet extends BasePacket { this.sourceMACAddress = MacAddress.of(sourceMACAddress); return this; } - + /** * @param sourceMACAddress the source MAC to set */ @@ -171,7 +173,7 @@ public class Ethernet extends BasePacket { this.etherType = etherType; return this; } - + /** * @return True if the Ethernet frame is broadcast, false otherwise */ @@ -179,7 +181,7 @@ public class Ethernet extends BasePacket { assert(destinationMACAddress.getLength() == 6); return destinationMACAddress.isBroadcast(); } - + /** * @return True is the Ethernet frame is multicast, False otherwise */ @@ -248,9 +250,9 @@ public class Ethernet extends BasePacket { bb.get(srcAddr); this.sourceMACAddress = MacAddress.of(srcAddr); - /* + /* * The ethertype is represented as 2 bytes in the packet header; - * however, EthType can only parse an int. Negative shorts (1 in + * however, EthType can only parse an int. Negative shorts (1 in * the most sig place b/c 2's complement) are still valid ethertypes, * but it doesn't appear this way unless we treat the sign bit as * part of an unsigned number. If we natively cast the short to an @@ -269,7 +271,7 @@ public class Ethernet extends BasePacket { this.vlanID = VLAN_UNTAGGED; } this.etherType = etherType; - + IPacket payload; if (Ethernet.etherTypeClassMap.containsKey((short) this.etherType.getValue())) { Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap.get((short) this.etherType.getValue()); @@ -321,7 +323,7 @@ public class Ethernet extends BasePacket { if (macBytes.length != 6) return false; for (int i = 0; i < 6; ++i) { - if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 || + if (HEXES.indexOf(macBytes[i].toUpperCase().charAt(0)) == -1 || HEXES.indexOf(macBytes[i].toUpperCase().charAt(1)) == -1) { return false; } @@ -333,7 +335,7 @@ public class Ethernet extends BasePacket { * Accepts a MAC address of the form 00:aa:11:bb:22:cc, case does not * matter, and returns a corresponding byte[]. * @param macAddress The MAC address to convert into a bye array - * @return The macAddress as a byte array + * @return The macAddress as a byte array */ public static byte[] toMACAddress(String macAddress) { return MacAddress.of(macAddress).getBytes(); @@ -358,7 +360,7 @@ public class Ethernet extends BasePacket { public static byte[] toByteArray(long macAddress) { return MacAddress.of(macAddress).getBytes(); } - + @Override public int hashCode() { final int prime = 31; @@ -410,7 +412,7 @@ public class Ethernet extends BasePacket { return false; return true; } - + /* (non-Javadoc) * @see java.lang.Object#toString(java.lang.Object) */ @@ -449,9 +451,9 @@ public class Ethernet extends BasePacket { if (pkt instanceof ARP) { ARP p = (ARP) pkt; sb.append("\nnw_src: "); - sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p.getSenderProtocolAddress()))); + sb.append(p.getSenderProtocolAddress().toString()); sb.append("\nnw_dst: "); - sb.append(IPv4.fromIPv4Address(IPv4.toIPv4Address(p.getTargetProtocolAddress()))); + sb.append(p.getTargetProtocolAddress().toString()); } else if (pkt instanceof LLDP) { sb.append("lldp packet"); @@ -474,6 +476,17 @@ public class Ethernet extends BasePacket { sb.append("\nnw_proto: "); sb.append(p.getProtocol()); } + else if (pkt instanceof IPv6) { + IPv6 p = (IPv6) pkt; + sb.append("\nnw_src: "); + sb.append(p.getSourceAddress().toString()); + sb.append("\nnw_dst: "); + sb.append(p.getDestinationAddress().toString()); + sb.append("\nnw_tclass: "); + sb.append(p.getTrafficClass()); + sb.append("\nnw_proto: "); + sb.append(p.getNextHeader().toString()); + } else if (pkt instanceof DHCP) { sb.append("\ndhcp packet"); } @@ -486,9 +499,8 @@ public class Ethernet extends BasePacket { else if (pkt instanceof BPDU) { sb.append("\nbpdu packet"); } - else sb.append("\nunknwon packet"); + else sb.append("\nunknown packet"); return sb.toString(); } - -} +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/packet/IPv6.java b/src/main/java/net/floodlightcontroller/packet/IPv6.java new file mode 100644 index 0000000000000000000000000000000000000000..e15b45e024fd9a4ecdf6aa9f9e1915a457ab8e5a --- /dev/null +++ b/src/main/java/net/floodlightcontroller/packet/IPv6.java @@ -0,0 +1,304 @@ +/** + * Copyright 2015, Big Switch Networks, Inc. + * Originally created by David Erickson, Stanford University + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ +package net.floodlightcontroller.packet; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.IpProtocol; + +/** + * @author Jacob Chappell (jacob.chappell@uky.edu) + * @edited Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu + */ +public class IPv6 extends BasePacket { + public static Map<IpProtocol, Class<? extends IPacket>> nextHeaderClassMap; + + static { + nextHeaderClassMap = new HashMap<IpProtocol, Class<? extends IPacket>>(); + // TODO: Add ICMPv6, IPv6 Options, etc.. + nextHeaderClassMap.put(IpProtocol.TCP, TCP.class); + nextHeaderClassMap.put(IpProtocol.UDP, UDP.class); + } + + public static final int HEADER_LENGTH = 40; + + protected byte version; + protected byte trafficClass; + protected int flowLabel; + protected short payloadLength; + protected IpProtocol nextHeader; + protected byte hopLimit; + protected IPv6Address sourceAddress; + protected IPv6Address destinationAddress; + + public IPv6() { + super(); + this.version = 6; + nextHeader = IpProtocol.NONE; + sourceAddress = IPv6Address.NONE; + destinationAddress = IPv6Address.NONE; + } + + public byte getVersion() { + return version; + } + + public IPv6 setVersion(byte version) { + this.version = version; + return this; + } + + public byte getTrafficClass() { + return trafficClass; + } + + public IPv6 setTrafficClass(byte trafficClass) { + this.trafficClass = trafficClass; + return this; + } + + public int getFlowLabel() { + return flowLabel; + } + + public IPv6 setFlowLabel(int flowLabel) { + this.flowLabel = flowLabel; + return this; + } + + public short getPayloadLength() { + return payloadLength; + } + + public IPv6 setPayloadLength(short payloadLength) { + this.payloadLength = payloadLength; + return this; + } + + public IpProtocol getNextHeader() { + return nextHeader; + } + + public IPv6 setNextHeader(IpProtocol nextHeader) { + this.nextHeader = nextHeader; + return this; + } + + public byte getHopLimit() { + return hopLimit; + } + + public IPv6 setHopLimit(byte hopLimit) { + this.hopLimit = hopLimit; + return this; + } + + public IPv6Address getSourceAddress() { + return sourceAddress; + } + + public IPv6 setSourceAddress(IPv6Address sourceAddress) { + this.sourceAddress = sourceAddress; + return this; + } + + public IPv6Address getDestinationAddress() { + return destinationAddress; + } + + public IPv6 setDestinationAddress(IPv6Address destinationAddress) { + this.destinationAddress = destinationAddress; + return this; + } + + @Override + public byte[] serialize() { + // Get the raw bytes of the payload we encapsulate. + byte[] payloadData = null; + if (this.payload != null) { + this.payload.setParent(this); + payloadData = this.payload.serialize(); + /* + * If we forgot to include the IpProtocol before serializing, + * try to ascertain what it is from the payload. If it's not + * a payload type we know about, we'll throw an exception. + */ + if ((this.nextHeader == null || this.nextHeader.equals(IpProtocol.NONE))) { + if (IPv6.nextHeaderClassMap.containsValue(this.payload.getClass())) { + Set<Map.Entry<IpProtocol, Class<? extends IPacket>>> entries = IPv6.nextHeaderClassMap.entrySet(); + for (Map.Entry<IpProtocol, Class<? extends IPacket>> m : entries) { + if (m.getValue().equals(this.payload.getClass())) { + this.setNextHeader(m.getKey()); + log.warn("Setting previously unset IPv6 'next header' to {} as detected by payload {}", m.getKey(), this.getPayload().getClass().toString()); + break; + } + } + } else if (this.payload instanceof Data) { + /* we're good -- there shouldn't be an IpProtocol set it it's just data */ + } else { + throw new IllegalArgumentException("IpProtocol is unset in IPv6 packet " + this.toString() + ". Unable to determine payload type to set for payload " + this.getPayload().getClass().toString()); + } + } + } + // Update our internal payload length. + this.payloadLength = (short) ((payloadData != null) ? payloadData.length : 0); + // Create a byte buffer to hold the IPv6 packet structure. + byte[] data = new byte[HEADER_LENGTH + this.payloadLength]; + ByteBuffer bb = ByteBuffer.wrap(data); + // Add header fields to the byte buffer in the correct order. + // Fear not the bit magic that must occur. + bb.put((byte) (((this.version & 0xF) << 4) | + ((this.trafficClass & 0xF0) >>> 4))); + bb.put((byte) (((this.trafficClass & 0xF) << 4) | + ((this.flowLabel & 0xF0000) >>> 16))); + bb.putShort((short) (this.flowLabel & 0xFFFF)); + bb.putShort(this.payloadLength); + bb.put((byte) this.nextHeader.getIpProtocolNumber()); + bb.put(this.hopLimit); + bb.put(this.sourceAddress.getBytes()); + bb.put(this.destinationAddress.getBytes()); + // Add the payload to the byte buffer, if necessary. + if (payloadData != null) + bb.put(payloadData); + // We're done! Return the data. + return data; + } + + @Override + public String toString() { + return "IPv6 [version=" + version + ", trafficClass=" + trafficClass + + ", flowLabel=" + flowLabel + ", payloadLength=" + + payloadLength + ", nextHeader=" + nextHeader + ", hopLimit=" + + hopLimit + ", sourceAddress=" + sourceAddress + + ", destinationAddress=" + destinationAddress + ", parent=" + + parent + ", payload=" + payload + "]"; + } + + @Override + public IPacket deserialize(byte[] data, int offset, int length) + throws PacketParsingException { + // Wrap the data in a byte buffer for easier retrieval. + ByteBuffer bb = ByteBuffer.wrap(data, offset, length); + // Retrieve values from IPv6 header. + byte firstByte = bb.get(); + byte secondByte = bb.get(); + this.version = (byte) ((firstByte & 0xF0) >>> 4); + if (this.version != 6) { + throw new PacketParsingException( + "Invalid version for IPv6 packet: " + + this.version); + } + this.trafficClass = (byte) (((firstByte & 0xF) << 4) | + ((secondByte & 0xF0) >>> 4)); + this.flowLabel = ((secondByte & 0xF) << 16) | + (bb.getShort() & 0xFFFF); + this.payloadLength = bb.getShort(); + this.nextHeader = IpProtocol.of(bb.get()); + this.hopLimit = bb.get(); + byte[] sourceAddress = new byte[16]; + bb.get(sourceAddress, 0, 16); + byte[] destinationAddress = new byte[16]; + bb.get(destinationAddress, 0, 16); + this.sourceAddress = IPv6Address.of(sourceAddress); + this.destinationAddress = IPv6Address.of(destinationAddress); + // Retrieve the payload, if possible. + IPacket payload; + if (IPv6.nextHeaderClassMap.containsKey(this.nextHeader)) { + Class<? extends IPacket> clazz = IPv6.nextHeaderClassMap.get(this.nextHeader); + try { + payload = clazz.newInstance(); + } catch (Exception e) { + throw new RuntimeException("Error parsing payload for IPv6 packet", e); + } + } else { + payload = new Data(); + } + // Deserialize as much of the payload as we can (hopefully all of it). + this.payload = payload.deserialize(data, bb.position(), + Math.min(this.payloadLength, bb.limit() - bb.position())); + this.payload.setParent(this); + // We're done! + return this; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime + * result + + ((destinationAddress == null) ? 0 : destinationAddress + .hashCode()); + result = prime * result + flowLabel; + result = prime * result + hopLimit; + result = prime * result + + ((nextHeader == null) ? 0 : nextHeader.hashCode()); + result = prime * result + payloadLength; + result = prime * result + + ((sourceAddress == null) ? 0 : sourceAddress.hashCode()); + result = prime * result + trafficClass; + result = prime * result + version; + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof IPv6)) + return false; + IPv6 other = (IPv6) obj; + if (destinationAddress == null) { + if (other.destinationAddress != null) + return false; + } else if (!destinationAddress.equals(other.destinationAddress)) + return false; + if (flowLabel != other.flowLabel) + return false; + if (hopLimit != other.hopLimit) + return false; + if (nextHeader == null) { + if (other.nextHeader != null) + return false; + } else if (!nextHeader.equals(other.nextHeader)) + return false; + if (payloadLength != other.payloadLength) + return false; + if (sourceAddress == null) { + if (other.sourceAddress != null) + return false; + } else if (!sourceAddress.equals(other.sourceAddress)) + return false; + if (trafficClass != other.trafficClass) + return false; + if (version != other.version) + return false; + return true; + } +} diff --git a/src/main/java/net/floodlightcontroller/packet/SPUD.java b/src/main/java/net/floodlightcontroller/packet/SPUD.java new file mode 100644 index 0000000000000000000000000000000000000000..d443d59dade46d471a5d75c7799c8e18cc719452 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/packet/SPUD.java @@ -0,0 +1,153 @@ +package net.floodlightcontroller.packet; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +/** + * @author Jacob Chappell (jacob.chappell@uky.edu) + */ +public class SPUD extends BasePacket { + public static final byte[] MAGIC_CONSTANT = + { (byte) 0xd8, 0x00, 0x00, (byte) 0xd8 }; + public static final int HEADER_LENGTH = 13; + public static final byte COMMAND_DATA = 0x0; + public static final byte COMMAND_OPEN = 0x1; + public static final byte COMMAND_CLOSE = 0x2; + public static final byte COMMAND_ACK = 0x3; + + protected long tubeID; + protected byte command; + protected boolean adec; + protected boolean pdec; + protected byte reserved; + + public long getTubeID() { + return tubeID; + } + + public SPUD setTubeID(long tubeID) { + this.tubeID = tubeID; + return this; + } + + public byte getCommand() { + return command; + } + + public SPUD setCommand(byte command) { + this.command = command; + return this; + } + + public boolean getADEC() { + return adec; + } + + public SPUD setADEC(boolean adec) { + this.adec = adec; + return this; + } + + public boolean getPDEC() { + return pdec; + } + + public SPUD setPDEC(boolean pdec) { + this.pdec = pdec; + return this; + } + + public byte getReserved() { + return reserved; + } + + public SPUD setReserved(byte reserved) { + this.reserved = reserved; + return this; + } + + @Override + public byte[] serialize() { + byte[] payloadData = null; + if (payload != null) { + payload.setParent(this); + payloadData = payload.serialize(); + } + int length = HEADER_LENGTH + ((payloadData == null) ? 0 : payloadData.length); + byte[] data = new byte[length]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.put(MAGIC_CONSTANT); + bb.putLong(tubeID); + byte adecBit = (byte) ((adec) ? 1 : 0); + byte pdecBit = (byte) ((pdec) ? 1 : 0); + byte lastByte = (byte) (((command & 0x3) << 6) | ((adecBit & 0x1) << 5) + | ((pdecBit & 0x1) << 4) | (reserved & 0xf)); + bb.put(lastByte); + if (payloadData != null) { + bb.put(payloadData); + } + return data; + } + + @Override + public IPacket deserialize(byte[] data, int offset, int length) + throws PacketParsingException { + ByteBuffer bb = ByteBuffer.wrap(data, offset, length); + byte[] magicConstant = new byte[MAGIC_CONSTANT.length]; + bb.get(magicConstant, 0, MAGIC_CONSTANT.length); + if (!Arrays.equals(magicConstant, MAGIC_CONSTANT)) { + throw new PacketParsingException("Magic constant is incorrect."); + } + tubeID = bb.getLong(); + byte lastByte = bb.get(); + command = (byte) ((lastByte & 0xc0) >>> 6); + adec = ((lastByte & 0x20) != 0); + pdec = ((lastByte & 0x10) != 0); + reserved = (byte) (lastByte & 0xF); + // TODO: make sure reserved bits are 0 for this version. + this.payload = new Data(); + this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); + this.payload.setParent(this); + return this; + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (adec ? 1231 : 1237); + result = prime * result + command; + result = prime * result + (pdec ? 1231 : 1237); + result = prime * result + reserved; + result = prime * result + (int) (tubeID ^ (tubeID >>> 32)); + return result; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof SPUD)) + return false; + SPUD other = (SPUD) obj; + if (adec != other.adec) + return false; + if (command != other.command) + return false; + if (pdec != other.pdec) + return false; + if (reserved != other.reserved) + return false; + if (tubeID != other.tubeID) + return false; + return true; + } +} diff --git a/src/main/java/net/floodlightcontroller/packet/UDP.java b/src/main/java/net/floodlightcontroller/packet/UDP.java index a5ee35f2c24971cca72c6d183bc2de2bea184f88..67302ae01aad3f2757a2ac9cade9856293c6e9cb 100644 --- a/src/main/java/net/floodlightcontroller/packet/UDP.java +++ b/src/main/java/net/floodlightcontroller/packet/UDP.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -18,6 +18,7 @@ package net.floodlightcontroller.packet; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -39,7 +40,7 @@ public class UDP extends BasePacket { */ UDP.decodeMap.put(DHCP_CLIENT_PORT, DHCP.class); UDP.decodeMap.put(DHCP_SERVER_PORT, DHCP.class); - + } protected TransportPort sourcePort; @@ -61,7 +62,7 @@ public class UDP extends BasePacket { this.sourcePort = sourcePort; return this; } - + /** * @param sourcePort the sourcePort to set */ @@ -84,7 +85,7 @@ public class UDP extends BasePacket { this.destinationPort = destinationPort; return this; } - + /** * @param destinationPort the destinationPort to set */ @@ -227,6 +228,14 @@ public class UDP extends BasePacket { this.destinationPort = TransportPort.of((int) (bb.getShort() & 0xffff)); // convert range 0 to 65534, not -32768 to 32767 this.length = bb.getShort(); this.checksum = bb.getShort(); + // Grab a snapshot of the first four bytes of the UDP payload. + // We will use these to see if the payload is SPUD, without + // disturbing the existing byte buffer's offsets. + ByteBuffer bb_spud = bb.slice(); + byte[] maybe_spud_bytes = new byte[SPUD.MAGIC_CONSTANT.length]; + if (bb_spud.remaining() >= SPUD.MAGIC_CONSTANT.length) { + bb_spud.get(maybe_spud_bytes, 0, SPUD.MAGIC_CONSTANT.length); + } if (UDP.decodeMap.containsKey(this.destinationPort)) { try { @@ -240,6 +249,9 @@ public class UDP extends BasePacket { } catch (Exception e) { throw new RuntimeException("Failure instantiating class", e); } + } else if (Arrays.equals(maybe_spud_bytes, SPUD.MAGIC_CONSTANT) + && bb.remaining() >= SPUD.HEADER_LENGTH) { + this.payload = new SPUD(); } else { this.payload = new Data(); } diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index 015b83a01c27b76ca7f0e3c9baead2fa71faf05c..175f464b25a974709caf1c47cbffe0916d132d7e 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.util.EnumSet; import java.util.ArrayList; import java.util.Comparator; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -51,6 +52,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.protocol.match.MatchField; import org.projectfloodlight.openflow.protocol.OFFlowModCommand; +import org.projectfloodlight.openflow.protocol.OFFlowModFlags; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFPacketOut; @@ -84,6 +86,8 @@ public abstract class ForwardingBase implements IOFMessageListener { public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1 + public static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false; + public static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true; public static boolean FLOWMOD_DEFAULT_MATCH_MAC = true; public static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true; @@ -255,8 +259,8 @@ public abstract class ForwardingBase implements IOFMessageListener { OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput(); List<OFAction> actions = new ArrayList<OFAction>(); - Match.Builder mb = MatchUtils.createRetentiveBuilder(match); - + Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion()); + // set input and output ports on the switch OFPort outPort = switchPortList.get(indx).getPortId(); OFPort inPort = switchPortList.get(indx - 1).getPortId(); @@ -265,6 +269,12 @@ public abstract class ForwardingBase implements IOFMessageListener { aob.setMaxLen(Integer.MAX_VALUE); actions.add(aob.build()); + if(FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG) { + Set<OFFlowModFlags> flags = new HashSet<>(); + flags.add(OFFlowModFlags.SEND_FLOW_REM); + fmb.setFlags(flags); + } + // compile fmb.setMatch(mb.build()) // was match w/o modifying input port .setActions(actions) diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java index 94a8686afe655dbfe2ff3ce3eb935c6fc14b24e1..36e8d8e16a0b3536c70d70cc4a6af58749dcab6f 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntries.java @@ -206,116 +206,375 @@ public class StaticFlowEntries { 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())); + if (match.supports(MatchField.IN_PORT) && match.isExact(MatchField.IN_PORT)) { + entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, match.get(MatchField.IN_PORT).toString()); + } else if (match.supportsMasked(MatchField.IN_PORT) && match.isPartiallyMasked(MatchField.IN_PORT)) { + entry.put(StaticFlowEntryPusher.COLUMN_IN_PORT, match.getMasked(MatchField.IN_PORT).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_IN_PORT, match.getVersion().toString()); + } break; case ETH_SRC: - entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.get(MatchField.ETH_SRC).toString()); + if (match.supports(MatchField.ETH_SRC) && match.isExact(MatchField.ETH_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.get(MatchField.ETH_SRC).toString()); + } else if (match.supportsMasked(MatchField.ETH_SRC) && match.isPartiallyMasked(MatchField.ETH_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_SRC, match.getMasked(MatchField.ETH_SRC).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_DL_SRC, match.getVersion().toString()); + } break; case ETH_DST: - entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.get(MatchField.ETH_DST).toString()); + if (match.supports(MatchField.ETH_DST) && match.isExact(MatchField.ETH_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.get(MatchField.ETH_DST).toString()); + } else if (match.supportsMasked(MatchField.ETH_DST) && match.isPartiallyMasked(MatchField.ETH_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_DST, match.getMasked(MatchField.ETH_DST).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_DL_DST, match.getVersion().toString()); + } break; case VLAN_VID: - entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.get(MatchField.VLAN_VID).getVlan()); + if (match.supports(MatchField.VLAN_VID) && match.isExact(MatchField.VLAN_VID)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.get(MatchField.VLAN_VID).toString()); + } else if (match.supportsMasked(MatchField.VLAN_VID) && match.isPartiallyMasked(MatchField.VLAN_VID)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN, match.getMasked(MatchField.VLAN_VID).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_DL_VLAN, match.getVersion().toString()); + } break; case VLAN_PCP: - entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, Byte.toString(match.get(MatchField.VLAN_PCP).getValue())); + if (match.supports(MatchField.VLAN_PCP) && match.isExact(MatchField.VLAN_PCP)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.get(MatchField.VLAN_PCP).toString()); + } else if (match.supportsMasked(MatchField.VLAN_PCP) && match.isPartiallyMasked(MatchField.VLAN_PCP)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.getMasked(MatchField.VLAN_PCP).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_DL_VLAN_PCP, match.getVersion().toString()); + } break; case ETH_TYPE: - entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.get(MatchField.ETH_TYPE).getValue()); + if (match.supports(MatchField.ETH_TYPE) && match.isExact(MatchField.ETH_TYPE)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.get(MatchField.ETH_TYPE).toString()); + } else if (match.supportsMasked(MatchField.ETH_TYPE) && match.isPartiallyMasked(MatchField.ETH_TYPE)) { + entry.put(StaticFlowEntryPusher.COLUMN_DL_TYPE, match.getMasked(MatchField.ETH_TYPE).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_DL_TYPE, match.getVersion().toString()); + } break; case IP_ECN: // TOS = [DSCP bits 0-5] + [ECN bits 6-7] --> bitwise OR to get TOS byte (have separate columns now though) - entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, Byte.toString(match.get(MatchField.IP_ECN).getEcnValue())); + if (match.supports(MatchField.IP_ECN) && match.isExact(MatchField.IP_ECN)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, match.get(MatchField.IP_ECN).toString()); + } else if (match.supportsMasked(MatchField.IP_ECN) && match.isPartiallyMasked(MatchField.IP_ECN)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_ECN, match.getMasked(MatchField.IP_ECN).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_NW_ECN, match.getVersion().toString()); + } break; case IP_DSCP: // Even for OF1.0, loxi will break ECN and DSCP up from the API's POV. This method is only invoked by a SFP service push from another module - entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, Byte.toString((byte) (match.get(MatchField.IP_DSCP).getDscpValue()))); + if (match.supports(MatchField.IP_DSCP) && match.isExact(MatchField.IP_DSCP)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, match.get(MatchField.IP_DSCP).toString()); + } else if (match.supportsMasked(MatchField.IP_DSCP) && match.isPartiallyMasked(MatchField.IP_DSCP)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_DSCP, match.getMasked(MatchField.IP_DSCP).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_NW_DSCP, match.getVersion().toString()); + } break; case IP_PROTO: - entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, Short.toString(match.get(MatchField.IP_PROTO).getIpProtocolNumber())); + if (match.supports(MatchField.IP_PROTO) && match.isExact(MatchField.IP_PROTO)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, match.get(MatchField.IP_PROTO).toString()); + } else if (match.supportsMasked(MatchField.IP_PROTO) && match.isPartiallyMasked(MatchField.IP_PROTO)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_PROTO, match.getMasked(MatchField.IP_PROTO).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_NW_PROTO, match.getVersion().toString()); + } break; case IPV4_SRC: - entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.get(MatchField.IPV4_SRC).toString()); + if (match.supports(MatchField.IPV4_SRC) && match.isExact(MatchField.IPV4_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.get(MatchField.IPV4_SRC).toString()); + } else if (match.supportsMasked(MatchField.IPV4_SRC) && match.isPartiallyMasked(MatchField.IPV4_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_SRC, match.getMasked(MatchField.IPV4_SRC).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_NW_SRC, match.getVersion().toString()); + } break; case IPV4_DST: - entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.get(MatchField.IPV4_DST).toString()); + if (match.supports(MatchField.IPV4_DST) && match.isExact(MatchField.IPV4_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.get(MatchField.IPV4_DST).toString()); + } else if (match.supportsMasked(MatchField.IPV4_DST) && match.isPartiallyMasked(MatchField.IPV4_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW_DST, match.getMasked(MatchField.IPV4_DST).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_NW_DST, match.getVersion().toString()); + } break; case TCP_SRC: - entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.get(MatchField.TCP_SRC).getPort()); + if (match.supports(MatchField.TCP_SRC) && match.isExact(MatchField.TCP_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.get(MatchField.TCP_SRC).toString()); + } else if (match.supportsMasked(MatchField.TCP_SRC) && match.isPartiallyMasked(MatchField.TCP_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_TCP_SRC, match.getMasked(MatchField.TCP_SRC).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_TCP_SRC, match.getVersion().toString()); + } break; case UDP_SRC: - entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.get(MatchField.UDP_SRC).getPort()); + if (match.supports(MatchField.UDP_SRC) && match.isExact(MatchField.UDP_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.get(MatchField.UDP_SRC).toString()); + } else if (match.supportsMasked(MatchField.UDP_SRC) && match.isPartiallyMasked(MatchField.UDP_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_UDP_SRC, match.getMasked(MatchField.UDP_SRC).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_UDP_SRC, match.getVersion().toString()); + } break; case SCTP_SRC: - entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.get(MatchField.SCTP_SRC).getPort()); + if (match.supports(MatchField.SCTP_SRC) && match.isExact(MatchField.SCTP_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.get(MatchField.SCTP_SRC).toString()); + } else if (match.supportsMasked(MatchField.SCTP_SRC) && match.isPartiallyMasked(MatchField.SCTP_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.getMasked(MatchField.SCTP_SRC).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_SCTP_SRC, match.getVersion().toString()); + } break; case TCP_DST: - entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.get(MatchField.TCP_DST).getPort()); + if (match.supports(MatchField.TCP_DST) && match.isExact(MatchField.TCP_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.get(MatchField.TCP_DST).toString()); + } else if (match.supportsMasked(MatchField.TCP_DST) && match.isPartiallyMasked(MatchField.TCP_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_TCP_DST, match.getMasked(MatchField.TCP_DST).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_TCP_DST, match.getVersion().toString()); + } break; case UDP_DST: - entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.get(MatchField.UDP_DST).getPort()); + if (match.supports(MatchField.UDP_DST) && match.isExact(MatchField.UDP_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.get(MatchField.UDP_DST).toString()); + } else if (match.supportsMasked(MatchField.UDP_DST) && match.isPartiallyMasked(MatchField.UDP_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_UDP_DST, match.getMasked(MatchField.UDP_DST).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_UDP_DST, match.getVersion().toString()); + } break; case SCTP_DST: - entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.get(MatchField.SCTP_DST).getPort()); + if (match.supports(MatchField.SCTP_DST) && match.isExact(MatchField.SCTP_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.get(MatchField.SCTP_DST).toString()); + } else if (match.supportsMasked(MatchField.SCTP_DST) && match.isPartiallyMasked(MatchField.SCTP_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_SCTP_DST, match.getMasked(MatchField.SCTP_DST).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_SCTP_DST, match.getVersion().toString()); + } break; case ICMPV4_TYPE: - entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.get(MatchField.ICMPV4_TYPE).getType()); + if (match.supports(MatchField.ICMPV4_TYPE) && match.isExact(MatchField.ICMPV4_TYPE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.get(MatchField.ICMPV4_TYPE).toString()); + } else if (match.supportsMasked(MatchField.ICMPV4_TYPE) && match.isPartiallyMasked(MatchField.ICMPV4_TYPE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.getMasked(MatchField.ICMPV4_TYPE).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ICMP_TYPE, match.getVersion().toString()); + } break; case ICMPV4_CODE: - entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.get(MatchField.ICMPV4_CODE).getCode()); + if (match.supports(MatchField.ICMPV4_CODE) && match.isExact(MatchField.ICMPV4_CODE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.get(MatchField.ICMPV4_CODE).toString()); + } else if (match.supportsMasked(MatchField.ICMPV4_CODE) && match.isPartiallyMasked(MatchField.ICMPV4_CODE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.getMasked(MatchField.ICMPV4_CODE).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ICMP_CODE, match.getVersion().toString()); + } break; case ARP_OP: - entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.get(MatchField.ARP_OP).getOpcode()); + if (match.supports(MatchField.ARP_OP) && match.isExact(MatchField.ARP_OP)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.get(MatchField.ARP_OP).toString()); + } else if (match.supportsMasked(MatchField.ARP_OP) && match.isPartiallyMasked(MatchField.ARP_OP)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.getMasked(MatchField.ARP_OP).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ARP_OPCODE, match.getVersion().toString()); + } break; case ARP_SHA: - entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.get(MatchField.ARP_SHA).toString()); + if (match.supports(MatchField.ARP_SHA) && match.isExact(MatchField.ARP_SHA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.get(MatchField.ARP_SHA).toString()); + } else if (match.supportsMasked(MatchField.ARP_SHA) && match.isPartiallyMasked(MatchField.ARP_SHA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SHA, match.getMasked(MatchField.ARP_SHA).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ARP_SHA, match.getVersion().toString()); + } break; case ARP_THA: - entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.get(MatchField.ARP_THA).toString()); + if (match.supports(MatchField.ARP_THA) && match.isExact(MatchField.ARP_THA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.get(MatchField.ARP_THA).toString()); + } else if (match.supportsMasked(MatchField.ARP_THA) && match.isPartiallyMasked(MatchField.ARP_THA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DHA, match.getMasked(MatchField.ARP_THA).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ARP_DHA, match.getVersion().toString()); + } break; case ARP_SPA: - entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.get(MatchField.ARP_SPA).toString()); + if (match.supports(MatchField.ARP_SPA) && match.isExact(MatchField.ARP_SPA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.get(MatchField.ARP_SPA).toString()); + } else if (match.supportsMasked(MatchField.ARP_SPA) && match.isPartiallyMasked(MatchField.ARP_SPA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_SPA, match.getMasked(MatchField.ARP_SPA).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ARP_SPA, match.getVersion().toString()); + } break; case ARP_TPA: - entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.get(MatchField.ARP_TPA).toString()); + if (match.supports(MatchField.ARP_TPA) && match.isExact(MatchField.ARP_TPA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.get(MatchField.ARP_TPA).toString()); + } else if (match.supportsMasked(MatchField.ARP_TPA) && match.isPartiallyMasked(MatchField.ARP_TPA)) { + entry.put(StaticFlowEntryPusher.COLUMN_ARP_DPA, match.getMasked(MatchField.ARP_TPA).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ARP_DPA, match.getVersion().toString()); + } break; case IPV6_SRC: - entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.get(MatchField.IPV6_SRC).toString()); + if (match.supports(MatchField.IPV6_SRC) && match.isExact(MatchField.IPV6_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.get(MatchField.IPV6_SRC).toString()); + } else if (match.supportsMasked(MatchField.IPV6_SRC) && match.isPartiallyMasked(MatchField.IPV6_SRC)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW6_SRC, match.getMasked(MatchField.IPV6_SRC).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_NW6_SRC, match.getVersion().toString()); + } break; case IPV6_DST: - entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.get(MatchField.IPV6_DST).toString()); + if (match.supports(MatchField.IPV6_DST) && match.isExact(MatchField.IPV6_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.get(MatchField.IPV6_DST).toString()); + } else if (match.supportsMasked(MatchField.IPV6_DST) && match.isPartiallyMasked(MatchField.IPV6_DST)) { + entry.put(StaticFlowEntryPusher.COLUMN_NW6_DST, match.getMasked(MatchField.IPV6_DST).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_NW6_DST, match.getVersion().toString()); + } break; case IPV6_FLABEL: - entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.get(MatchField.IPV6_FLABEL).toString()); + if (match.supports(MatchField.IPV6_FLABEL) && match.isExact(MatchField.IPV6_FLABEL)) { + entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.get(MatchField.IPV6_FLABEL).toString()); + } else if (match.supportsMasked(MatchField.IPV6_FLABEL) && match.isPartiallyMasked(MatchField.IPV6_FLABEL)) { + entry.put(StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.getMasked(MatchField.IPV6_FLABEL).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_IPV6_FLOW_LABEL, match.getVersion().toString()); + } break; case ICMPV6_TYPE: - entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, String.valueOf(match.get(MatchField.ICMPV6_TYPE).getValue())); + if (match.supports(MatchField.ICMPV6_TYPE) && match.isExact(MatchField.ICMPV6_TYPE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.get(MatchField.ICMPV6_TYPE).toString()); + } else if (match.supportsMasked(MatchField.ICMPV6_TYPE) && match.isPartiallyMasked(MatchField.ICMPV6_TYPE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.getMasked(MatchField.ICMPV6_TYPE).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ICMP6_TYPE, match.getVersion().toString()); + } break; case ICMPV6_CODE: - entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.get(MatchField.ICMPV6_CODE).getValue()); + if (match.supports(MatchField.ICMPV6_CODE) && match.isExact(MatchField.ICMPV6_CODE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.get(MatchField.ICMPV6_CODE).toString()); + } else if (match.supportsMasked(MatchField.ICMPV6_CODE) && match.isPartiallyMasked(MatchField.ICMPV6_CODE)) { + entry.put(StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.getMasked(MatchField.ICMPV6_CODE).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ICMP6_CODE, match.getVersion().toString()); + } break; case IPV6_ND_SLL: - entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.get(MatchField.IPV6_ND_SLL).toString()); - break; + if (match.supports(MatchField.IPV6_ND_SLL) && match.isExact(MatchField.IPV6_ND_SLL)) { + entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.get(MatchField.IPV6_ND_SLL).toString()); + } else if (match.supportsMasked(MatchField.IPV6_ND_SLL) && match.isPartiallyMasked(MatchField.IPV6_ND_SLL)) { + entry.put(StaticFlowEntryPusher.COLUMN_ND_SLL, match.getMasked(MatchField.IPV6_ND_SLL).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ND_SLL, match.getVersion().toString()); + } + break; case IPV6_ND_TLL: - entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.get(MatchField.IPV6_ND_TLL).toString()); + if (match.supports(MatchField.IPV6_ND_TLL) && match.isExact(MatchField.IPV6_ND_TLL)) { + entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.get(MatchField.IPV6_ND_TLL).toString()); + } else if (match.supportsMasked(MatchField.IPV6_ND_TLL) && match.isPartiallyMasked(MatchField.IPV6_ND_TLL)) { + entry.put(StaticFlowEntryPusher.COLUMN_ND_TLL, match.getMasked(MatchField.IPV6_ND_TLL).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ND_TLL, match.getVersion().toString()); + } break; case IPV6_ND_TARGET: - entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.get(MatchField.IPV6_ND_TARGET).toString()); + if (match.supports(MatchField.IPV6_ND_TARGET) && match.isExact(MatchField.IPV6_ND_TARGET)) { + entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.get(MatchField.IPV6_ND_TARGET).toString()); + } else if (match.supportsMasked(MatchField.IPV6_ND_TARGET) && match.isPartiallyMasked(MatchField.IPV6_ND_TARGET)) { + entry.put(StaticFlowEntryPusher.COLUMN_ND_TARGET, match.getMasked(MatchField.IPV6_ND_TARGET).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_ND_TARGET, match.getVersion().toString()); + } break; case MPLS_LABEL: - entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.get(MatchField.MPLS_LABEL).getValue()); + if (match.supports(MatchField.MPLS_LABEL) && match.isExact(MatchField.MPLS_LABEL)) { + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.get(MatchField.MPLS_LABEL).toString()); + } else if (match.supportsMasked(MatchField.MPLS_LABEL) && match.isPartiallyMasked(MatchField.MPLS_LABEL)) { + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.getMasked(MatchField.MPLS_LABEL).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_MPLS_LABEL, match.getVersion().toString()); + } break; case MPLS_TC: - entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.get(MatchField.MPLS_TC).getValue()); + if (match.supports(MatchField.MPLS_TC) && match.isExact(MatchField.MPLS_TC)) { + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.get(MatchField.MPLS_TC).toString()); + } else if (match.supportsMasked(MatchField.MPLS_TC) && match.isPartiallyMasked(MatchField.MPLS_TC)) { + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_TC, match.getMasked(MatchField.MPLS_TC).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_MPLS_TC, match.getVersion().toString()); + } break; case MPLS_BOS: - entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.get(MatchField.MPLS_BOS).getValue()); + if (match.supports(MatchField.MPLS_BOS) && match.isExact(MatchField.MPLS_BOS)) { + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.get(MatchField.MPLS_BOS).toString()); + } else if (match.supportsMasked(MatchField.MPLS_BOS) && match.isPartiallyMasked(MatchField.MPLS_BOS)) { + entry.put(StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.getMasked(MatchField.MPLS_BOS).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_MPLS_BOS, match.getVersion().toString()); + } break; case METADATA: - entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.get(MatchField.METADATA).getValue().getValue()); + if (match.supports(MatchField.METADATA) && match.isExact(MatchField.METADATA)) { + entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.get(MatchField.METADATA).toString()); + } else if (match.supportsMasked(MatchField.METADATA) && match.isPartiallyMasked(MatchField.METADATA)) { + entry.put(StaticFlowEntryPusher.COLUMN_METADATA, match.getMasked(MatchField.METADATA).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_METADATA, match.getVersion().toString()); + } break; case TUNNEL_ID: - entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.get(MatchField.TUNNEL_ID).getValue()); - break; + if (match.supports(MatchField.TUNNEL_ID) && match.isExact(MatchField.TUNNEL_ID)) { + entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.get(MatchField.TUNNEL_ID).toString()); + } else if (match.supportsMasked(MatchField.TUNNEL_ID) && match.isPartiallyMasked(MatchField.TUNNEL_ID)) { + entry.put(StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.getMasked(MatchField.TUNNEL_ID).toString()); + } else { + log.error("Got match for {} but protocol {} does not support said match. Ignoring match.", + StaticFlowEntryPusher.COLUMN_TUNNEL_ID, match.getVersion().toString()); + } + break; // case PBB_ISID not implemented in loxi default: log.error("Unhandled Match when parsing OFFlowMod: {}, {}", mf, mf.id); diff --git a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java index 89f351627bc8993bd321f66b332263410a46a41d..e0f33f14be22fa12358503e06007abd561068f85 100644 --- a/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java +++ b/src/main/java/net/floodlightcontroller/staticflowentry/StaticFlowEntryPusher.java @@ -362,7 +362,14 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, } // get the correct builder for the OF version supported by the switch - fmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildFlowModify(); + try { + fmb = OFFactories.getFactory(switchService.getSwitch(DatapathId.of(switchName)).getOFFactory().getVersion()).buildFlowModify(); + } catch (NullPointerException e) { + /* switch was not connected/known */ + storageSourceService.deleteRowAsync(TABLE_NAME, entryName); + log.error("Deleting entry {}. Switch {} was not connected to the controller, and we need to know the OF protocol version to compose the flow mod.", entryName, switchName); + return; + } StaticFlowEntries.initDefaultFlowMod(fmb, entryName); @@ -587,16 +594,20 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, } // send flow_mod delete - OFFlowDeleteStrict flowMod = FlowModUtils.toFlowDeleteStrict(entriesFromStorage.get(dpid).get(entryName)); + if (switchService.getSwitch(DatapathId.of(dpid)) != null) { + OFFlowDeleteStrict flowMod = FlowModUtils.toFlowDeleteStrict(entriesFromStorage.get(dpid).get(entryName)); + + if (entriesFromStorage.containsKey(dpid) && entriesFromStorage.get(dpid).containsKey(entryName)) { + entriesFromStorage.get(dpid).remove(entryName); + } else { + log.debug("Tried to delete non-existent entry {} for switch {}", entryName, dpid); + return; + } - if (entriesFromStorage.containsKey(dpid) && entriesFromStorage.get(dpid).containsKey(entryName)) { - entriesFromStorage.get(dpid).remove(entryName); + writeFlowModToSwitch(DatapathId.of(dpid), flowMod); } else { - log.debug("Tried to delete non-existent entry {} for switch {}", entryName, dpid); - return; + log.debug("Not sending flow delete for disconnected switch."); } - - writeFlowModToSwitch(DatapathId.of(dpid), flowMod); return; } @@ -730,19 +741,19 @@ implements IOFSwitchListener, IFloodlightModule, IStaticFlowEntryPusherService, Map<String, OFFlowMod> flowsByName = getFlows(sw.getId()); for (Map.Entry<String, OFFlowMod> entry : flowsByName.entrySet()) { if (msg.getCookie().equals(entry.getValue().getCookie()) && - msg.getHardTimeout() == entry.getValue().getHardTimeout() && + (msg.getVersion().compareTo(OFVersion.OF_12) < 0 ? true : msg.getHardTimeout() == entry.getValue().getHardTimeout()) && msg.getIdleTimeout() == entry.getValue().getIdleTimeout() && msg.getMatch().equals(entry.getValue().getMatch()) && msg.getPriority() == entry.getValue().getPriority() && - msg.getTableId().equals(entry.getValue().getTableId()) - ) { + (msg.getVersion().compareTo(OFVersion.OF_10) == 0 ? true : msg.getTableId().equals(entry.getValue().getTableId())) + ) { flowToRemove = entry.getKey(); break; } } - + log.debug("Flow to Remove: {}", flowToRemove); - + /* * Remove the flow. This will send the delete message to the switch, * since we cannot tell the storage listener rowsdeleted() that we diff --git a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java index 85fbf0f46fa1fee79dda7f06e2ca590fb7c519cb..4c16876ec66614833d154b9aa351666c8cfb07b4 100644 --- a/src/main/java/net/floodlightcontroller/testmodule/TestModule.java +++ b/src/main/java/net/floodlightcontroller/testmodule/TestModule.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutionException; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; @@ -16,6 +17,8 @@ import org.projectfloodlight.openflow.protocol.OFMeterBandType; import org.projectfloodlight.openflow.protocol.OFMeterConfig; import org.projectfloodlight.openflow.protocol.OFMeterMod; import org.projectfloodlight.openflow.protocol.OFMeterModCommand; +import org.projectfloodlight.openflow.protocol.OFMeterStats; +import org.projectfloodlight.openflow.protocol.OFMeterStatsReply; import org.projectfloodlight.openflow.protocol.OFMeterStatsRequest; import org.projectfloodlight.openflow.protocol.OFOxmClass; import org.projectfloodlight.openflow.protocol.OFPortDesc; @@ -76,6 +79,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ListenableFuture; import net.floodlightcontroller.core.IOFSwitchListener; import net.floodlightcontroller.core.PortChangeType; @@ -141,6 +145,20 @@ public class TestModule implements IFloodlightModule, IOFSwitchListener { if (switchId.equals(DatapathId.of(1))) switchService.getSwitch(switchId).write(fab.build());*/ + /*OFMeterStatsRequest req; + ListenableFuture<OFMeterStatsReply> reply = switchService.getActiveSwitch(switchId).writeRequest(req); + try { + for (OFMeterStats entry : reply.get().getEntries()) { + U64 byteInCount = entry.getByteInCount(); + for (OFMeterBandStats mbs : entry.getBandStats()) { + U64 byteBandCount = mbs.getByteBandCount(); + U64 pktBandCount = mbs.getPacketBandCount(); + } + } + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + }*/ + OFFactory f =factory; Match.Builder mb =f.buildMatch(); mb.setExact(MatchField.ETH_SRC, MacAddress.of(2)); diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java index cf2183a2e8d1f016a68496a4e9cffc38e5dec064..7568dad7d11139915ac8b480eca1cb837d677005 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java @@ -91,7 +91,7 @@ public class TopologyInstance { @Override public Route load(RouteId rid) { - return ti.buildroute(rid); + return ti.buildroute(rid); } } @@ -638,7 +638,7 @@ public class TopologyInstance { if (log.isTraceEnabled()) { log.trace("buildroute: {}", result); } - return result; + return result; } protected int getCost(DatapathId srcId, DatapathId dstId) { diff --git a/src/main/java/net/floodlightcontroller/util/MatchUtils.java b/src/main/java/net/floodlightcontroller/util/MatchUtils.java index ea3d77cf26075acbd1c602052c66e25c04b0156c..c458a9f2e852e5ac79827828de0a8ce2b3bf4054 100644 --- a/src/main/java/net/floodlightcontroller/util/MatchUtils.java +++ b/src/main/java/net/floodlightcontroller/util/MatchUtils.java @@ -11,6 +11,7 @@ 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.IPv6AddressWithMask; import org.projectfloodlight.openflow.types.IPv6FlowLabel; @@ -104,6 +105,8 @@ public class MatchUtils { public static final String STR_METADATA = "metadata"; public static final String STR_TUNNEL_ID = "tunnel_id"; + public static final String STR_TUNNEL_IPV4_SRC = "tunnel_ipv4_src"; + public static final String STR_TUNNEL_IPV4_DST = "tunnel_ipv4_dst"; public static final String STR_PBB_ISID = "pbb_isid"; @@ -152,27 +155,27 @@ public class MatchUtils { } return mb.build(); } - + + /** + * Retains all fields in the Match parent. Converts the parent to an + * equivalent Match of OFVersion version. No polite check is done to verify + * if MatchFields in parent are supported in a Match of OFVersion + * version. An exception will be thrown if there are any unsupported + * fields during the conversion process. * - * Workaround for bug in Loxi: - * - * Create a builder from an existing Match object. Unlike Match's - * createBuilder(), this utility function will preserve all of - * Match m's MatchFields, even if new MatchFields are set or modified - * with the builder after it is returned to the calling function. - * - * All original MatchFields in m will be set if the build() method is - * invoked upon the returned builder. After the builder is returned, if - * a MatchField is modified via setExact(), setMasked(), or wildcard(), - * the newly modified MatchField will replace the original found in m. + * Note that a Match.Builder is returned. This is a convenience for cases + * where MatchFields might be modified, added, or removed prior to being + * built (e.g. in Forwarding/Routing between switches of different OFVersions). + * Simply build the returned Match.Builder if you would like to treat this + * function as a strict copy-to-version. * - * @param m; the match to create the builder from - * @return Match.Builder; the builder that can be modified, and when built, - * will retain all of m's MatchFields, unless you explicitly overwrite them. + * @param parent, the Match to convert + * @param version, the OFVersion to convert parent to + * @return a Match.Builder of the newly-converted Match */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static Match.Builder createRetentiveBuilder(Match m) { + @SuppressWarnings("unchecked") + public static Match.Builder convertToVersion(Match parent, OFVersion version) { /* Builder retains a parent MatchField list, but list will not be used to * build the new match if the builder's set methods have been invoked; only * additions will be built, and all parent MatchFields will be ignored, @@ -182,14 +185,15 @@ public class MatchUtils { * in the Match built from this builder if the user decides to add or subtract * from the MatchField list. */ - Match.Builder mb = m.createBuilder(); - Iterator<MatchField<?>> itr = m.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded) + Match.Builder mb = OFFactories.getFactory(version).buildMatch(); + Iterator<MatchField<?>> itr = parent.getMatchFields().iterator(); // only get exact or masked fields (not fully wildcarded) while(itr.hasNext()) { + @SuppressWarnings("rawtypes") MatchField mf = itr.next(); - if (m.isExact(mf)) { - mb.setExact(mf, m.get(mf)); - } else if (m.isPartiallyMasked(mf)) { - mb.setMasked(mf, m.getMasked(mf)); + if (parent.isExact(mf)) { + mb.setExact(mf, parent.get(mf)); + } else if (parent.isPartiallyMasked(mf)) { + mb.setMasked(mf, parent.getMasked(mf)); } else { // it's either exact, masked, or wildcarded // itr only contains exact and masked MatchFields @@ -199,6 +203,28 @@ public class MatchUtils { return mb; } + /** + * + * Workaround for bug in Loxi: + * + * Create a builder from an existing Match object. Unlike Match's + * createBuilder(), this utility function will preserve all of + * Match m's MatchFields, even if new MatchFields are set or modified + * with the builder after it is returned to the calling function. + * + * All original MatchFields in m will be set if the build() method is + * invoked upon the returned builder. After the builder is returned, if + * a MatchField is modified via setExact(), setMasked(), or wildcard(), + * the newly modified MatchField will replace the original found in m. + * + * @param m; the match to create the builder from + * @return Match.Builder; the builder that can be modified, and when built, + * will retain all of m's MatchFields, unless you explicitly overwrite them. + */ + public static Match.Builder createRetentiveBuilder(Match m) { + return convertToVersion(m, m.getVersion()); + } + /** * 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 @@ -748,6 +774,20 @@ public class MatchUtils { dataMask[1].contains("0x") ? U64.of(Long.valueOf(dataMask[1].replaceFirst("0x", ""), 16)) : U64.of(Long.valueOf(dataMask[1]))); } break; + case STR_TUNNEL_IPV4_SRC: + if (dataMask.length == 1) { + mb.setExact(MatchField.TUNNEL_IPV4_SRC, IPv4Address.of(key_value[1])); + } else { + mb.setMasked(MatchField.TUNNEL_IPV4_SRC, IPv4AddressWithMask.of(key_value[1])); + } + break; + case STR_TUNNEL_IPV4_DST: + if (dataMask.length == 1) { + mb.setExact(MatchField.TUNNEL_IPV4_DST, IPv4Address.of(key_value[1])); + } else { + mb.setMasked(MatchField.TUNNEL_IPV4_DST, IPv4AddressWithMask.of(key_value[1])); + } + break; case STR_PBB_ISID: /*TODO no-op. Not implemented. if (key_value[1].startsWith("0x")) { diff --git a/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..f2744985b46813c045828174c8142dab3f418e54 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/OFDPAUtils.java @@ -0,0 +1,784 @@ +package net.floodlightcontroller.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import javax.annotation.Nonnull; + +import net.floodlightcontroller.core.IOFSwitch; + +import org.projectfloodlight.openflow.protocol.OFBucket; +import org.projectfloodlight.openflow.protocol.OFFlowAdd; +import org.projectfloodlight.openflow.protocol.OFGroupAdd; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; +import org.projectfloodlight.openflow.protocol.match.Match; +import org.projectfloodlight.openflow.protocol.match.MatchField; +import org.projectfloodlight.openflow.protocol.match.MatchFields; +import org.projectfloodlight.openflow.types.OFBufferId; +import org.projectfloodlight.openflow.types.OFGroup; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.U16; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.VlanVid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The Broadcom OF-DPA 2.0 specification provides an interesting + * packet processing pipeline. Most OpenFlow programmers are + * accustomed to programming Open vSwitch or other software switches, + * where much if not all of the OpenFlow specification is supported. + * + * Hardware switch vendors wishing to support OpenFlow typically mimic + * software switches and allow programmers access to uniform flow + * tables, where the switch will automatically move operations not + * supported in hardware to software/the CPU, also known as the + * "slow path." In order to operate on packets in hardware, switch + * vendors need to expose some of the low-level details, features, and + * shortcomings to the programmer. That is where OF-DPA comes in. + * + * On compatible Broadcom-based switches, in conjunction with the Indigo + * OpenFlow agent running onboard the switch, OpenFlow controllers can + * leverage complete hardware forwarding of packets in the data plane. + * This does not necessarily include the ability to perform all actions + * and matches in hardware; however, it increases the available set of + * actions and matches and give the controller access to powerful + * features onboard the switch, such as routing and tunneling. + * + * OFDPAUtils provides an abstraction when programming a Broadcom + * OF-DPA switch in order to ease conformance to the OF-DPA 2.0 spec + * from the controller and modules. + * + * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu + */ +public class OFDPAUtils { + + private OFDPAUtils() {} + + private static final Logger log = LoggerFactory.getLogger(OFDPAUtils.class); + + /* These cannot be changed, since they're final (public == okay for the ones we want to inform about) */ + public static final int PRIORITY = 1000; + public static final int DLF_PRIORITY = 0; + public static final int HARD_TIMEOUT = 0; + public static final int IDLE_TIMEOUT = 0; + private static final long APP_COOKIE = 1000; + public static final U64 COOKIE = U64.of(APP_COOKIE); + + private static class OFDPAGroupType { + static final int L2_INTERFACE = 0; /* 0 */ + static final int L2_REWRITE = 1; /* 1 */ + static final int L3_UNICAST = 2; /* 2 */ + static final int L2_MULTICAST = 3; /* 3 */ + static final int L2_FLOOD = 4; /* 4 */ + static final int L3_INTERFACE = 5; /* 5 */ + static final int L3_MULTICAST = 6; /* 6 */ + static final int L3_ECMP = 7; /* 7 */ + static final int L2_DATA_CENTER_OVERLAY = 8; /* 8 */ + static final int MPLS_LABEL = 9; /* 9 */ + static final int MPLS_FORWARDING = 10; /* 10 */ + static final int L2_UNFILTERED_INTERFACE = 11; /* 11 */ + static final int L2_LOOPBACK = 12; /* 12 */ + } + + private static class L2OverlaySubType { + static final int L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS = 0; + static final int L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS = 1; + static final int L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS = 2; + static final int L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS = 3; + } + + private static class MPLSSubType { + static final int MPLS_INTERFACE = 0; + static final int MPLS_L2_VPN_LABEL = 1; + static final int MPLS_L3_VPN_LABEL = 2; + static final int MPLS_TUNNEL_LABEL_1 = 3; + static final int MPLS_TUNNEL_LABEL_2 = 4; + static final int MPLS_SWAP_LABEL = 5; + static final int MPLS_FAST_FAILOVER = 6; + static final int MPLS_ECMP = 8; + static final int MPLS_L2_TAG = 10; + } + + public static class Tables { + public static final TableId INGRESS_PORT = TableId.of(0); + public static final TableId VLAN = TableId.of(10); + public static final TableId TERMINATION_MAC = TableId.of(20); + public static final TableId UNICAST_ROUTING = TableId.of(30); + public static final TableId MULITCAST_ROUTING = TableId.of(40); + public static final TableId BRIDGING = TableId.of(50); + public static final TableId POLICY_ACL = TableId.of(60); + } + + private static final List<MatchFields> ALLOWED_MATCHES = + Collections.unmodifiableList( + Arrays.asList( + MatchFields.IN_PORT, + MatchFields.ETH_SRC, + MatchFields.ETH_DST, + MatchFields.ETH_TYPE, + MatchFields.VLAN_VID, + MatchFields.VLAN_PCP, + MatchFields.TUNNEL_ID, + MatchFields.IP_PROTO, + MatchFields.IPV4_SRC, + MatchFields.IPV4_DST, + MatchFields.IP_DSCP, + MatchFields.IP_ECN, + MatchFields.ARP_SPA, + MatchFields.ICMPV4_CODE, + MatchFields.ICMPV4_TYPE, + + MatchFields.IPV6_SRC, + MatchFields.IPV6_DST, + MatchFields.IPV6_FLABEL, + MatchFields.ICMPV6_CODE, + MatchFields.ICMPV6_TYPE, + + MatchFields.TCP_SRC, + MatchFields.TCP_DST, + MatchFields.UDP_SRC, + MatchFields.UDP_DST, + MatchFields.SCTP_SRC, + MatchFields.SCTP_DST + ) + ); + + /** + * Determine whether or not the provided switch is an OF-DPA switch. + * + * @param sw, the switch to check + * @return true if the switch is an OF-DPA switch; false otherwise + */ + public static boolean isOFDPASwitch(IOFSwitch s) { + /* + * Many switches might have the same table IDs as an OF-DPA switch, + * so we can't check the IDs only. Alternatively, we can check the + * name, description, etc. to see if it aligns with an OF-DPA-set + * description. This isn't fool-proof, but it'll work for now. + */ + if (s.getSwitchDescription().getSoftwareDescription().toLowerCase().contains("of-dpa")) { + return true; + } else { + return false; + } + } + + /** + * Get the MatchFields that an OF-DPA switch supports matching. + * Note that this does not specify match codependencies or + * mutually exclusive matches. + * + * @return, an unmodifiable list of potential MatchFields + */ + public static List<MatchFields> getSupportedMatchFields() { + return ALLOWED_MATCHES; + } + + /** + * Examine all the MatchFields in a Match object and pick out + * the MatchFields that are not supported by OF-DPA. + * + * @param m + * @return + */ + public static List<MatchFields> checkMatchFields(Match m) { + List<MatchFields> unsupported = null; + Iterator<MatchField<?>> mfi = m.getMatchFields().iterator(); + + while (mfi.hasNext()) { + MatchField<?> mf = mfi.next(); + if (!getSupportedMatchFields().contains(mf.id)) { + if (unsupported == null) { + unsupported = new ArrayList<MatchFields>(); + } + unsupported.add(mf.id); + } + } + + return unsupported; + } + + /** + * Add the OFDPA groups and flows necessary to facilitate future forwarding/learning + * switch flows. The switch provided must be an OFDPA 2.0 compliant switch. + * Use VLAN tag of null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is + * reserved as an internal VLAN. + * + * This function will add the flows that permit all packets in the VLAN specified and + * on the ports specified to reach the policy ACL table. The policy ACL table (60) of + * the OF-DPA switch will contain a DLF, zero-priority flow to forward all packets to + * the controller for processing. A packet forwarded to the controller from the policy + * ACL table can be either handled manually or have a flow inserted for it via + * {@link OFDPAUtils#addBridgingFlow(IOFSwitch, U64, int, int, int, Match, VlanVid, OFPort) this function}. + * + * @param sw + * @param vlan + * @param ports + * @return + */ + public static boolean addLearningSwitchPrereqs(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) { + /* + * Both of these must complete. If the first fails, the second will not be executed. (AND short-circuit) + * + * Groups must be added last, since they require the VLANs to be added first. + */ + return addLearningSwitchPrereqFlows(sw, vlan, ports) && addLearningSwitchPrereqGroups(sw, vlan, ports) ; + } + + /** + * Note: Prefer {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function} + * over this function unless you know what you are doing. + * + * Add the OFDPA groups necessary to facilitate future forwarding/learning + * switch flows. The switch provided must be an OFDPA 2.0 compliant switch. + * If a VLAN tag is provided, it will be expected that all ports in the + * accompanying list of ports are tagged and not access. Use VLAN tag of + * null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is + * reserved as an internal VLAN. + * + * @param sw + * @param vlan + * @param ports + * @return + */ + public static boolean addLearningSwitchPrereqGroups(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) { + if (sw == null) { + throw new NullPointerException("Switch cannot be null."); + } + if (vlan == null) { + vlan = VlanVid.ZERO; /* set untagged */ + } else if (vlan.equals(VlanVid.ofVlan(1))) { + throw new IllegalArgumentException("VLAN cannot be 1. VLAN 1 is an reserved VLAN for internal use inside the OFDPA switch."); + } + if (ports == null) { + throw new NullPointerException("List of ports cannot be null. Must specify at least 2 valid switch ports."); + } else if (ports.size() < 2) { + throw new IllegalArgumentException("List of ports must contain at least 2 valid switch ports."); + } else { + /* verify ports are valid switch ports */ + for (OFPortModeTuple p : ports) { + if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) { + throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString()); + } else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getPortNumber() > 0xffFFff00)) { + throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString()); + } + } + } + + /* + * For each output port, add an L2 interface group. + */ + for (OFPortModeTuple p : ports) { + List<OFAction> actions = new ArrayList<OFAction>(); + if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* if it's untagged (internal=1) or access, pop the tag */ + actions.add(sw.getOFFactory().actions().popVlan()); + } + actions.add(sw.getOFFactory().actions().output(p.getPort(), 0xffFFffFF)); + + OFGroupAdd ga = sw.getOFFactory().buildGroupAdd() + .setGroup(GroupIds.createL2Interface(p.getPort(), (vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))) + .setGroupType(OFGroupType.INDIRECT) + .setBuckets(Collections.singletonList( + sw.getOFFactory().buildBucket() + .setActions(actions) + .build())) + .build(); + sw.write(ga); + } + + /* + * For the VLAN provided (or internal VLAN 1 if untagged), + * add an L2 flood group. + */ + List<OFBucket> bucketList = new ArrayList<OFBucket>(ports.size()); + for (OFPortModeTuple p : ports) { + List<OFAction> actions = new ArrayList<OFAction>(); + if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* ditto */ + actions.add(sw.getOFFactory().actions().popVlan()); + } + actions.add(sw.getOFFactory().actions().output(p.getPort(), 0xffFFffFF)); + bucketList.add(sw.getOFFactory().buildBucket().setActions(actions).build()); + } + OFGroupAdd ga = sw.getOFFactory().buildGroupAdd() /* use the VLAN ID as the group ID */ + .setGroup(GroupIds.createL2Flood(U16.of(vlan.getVlan()), vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)) + .setGroupType(OFGroupType.INDIRECT) + .setBuckets(bucketList) + .build(); + sw.write(ga); + + return true; + } + + /** + * Note: Prefer {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) this function} + * over this function unless you know what you are doing. + * + * Add the OFDPA flows necessary to facilitate future forwarding/learning + * switch flows. The switch provided must be an OFDPA 2.0 compliant switch. + * If a VLAN tag is provided, it will be expected that all ports in the + * accompanying list of ports are tagged and not access. Use VLAN tag of + * null or VlanVid.ZERO for untagged. VLAN tag 1 may not be used; it is + * reserved as an internal VLAN. + * + * @param sw + * @param vlan + * @param ports + * @return + */ + public static boolean addLearningSwitchPrereqFlows(@Nonnull IOFSwitch sw, VlanVid vlan, @Nonnull List<OFPortModeTuple> ports) { + if (sw == null) { + throw new NullPointerException("Switch cannot be null."); + } + if (vlan == null) { + vlan = VlanVid.ZERO; /* set untagged */ + } else if (vlan.equals(VlanVid.ofVlan(1))) { + throw new IllegalArgumentException("VLAN cannot be 1. VLAN 1 is an reserved VLAN for internal use inside the OFDPA switch."); + } + if (ports == null) { + throw new NullPointerException("List of ports cannot be null. Must specify at least 2 valid switch ports."); + } else if (ports.size() < 2) { + throw new IllegalArgumentException("List of ports must contain at least 2 valid switch ports."); + } else { + /* verify ports are valid switch ports */ + for (OFPortModeTuple p : ports) { + if (sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getShortPortNumber() > 0xFF00)) { + throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString()); + } else if (!sw.getOFFactory().getVersion().equals(OFVersion.OF_10) && (sw.getPort(p.getPort()) == null || p.getPort().getPortNumber() > 0xffFFff00)) { + throw new IllegalArgumentException("Port " + p.getPort().getPortNumber() + " is not a valid port on switch " + sw.getId().toString()); + } + } + } + + /* + * VLAN flow table (10) will drop by default, so we need to + * add a flow that will always direct the packet to the next + * flow table, the termination MAC table (20). If a VLAN tag + * is not present, then we need to append a tag. The tag to + * append will either be the tag specified in outVlan or the + * default tag of 1. Only VLANs are handled in this table. + */ + ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>(); + ArrayList<OFAction> actions = new ArrayList<OFAction>(); + Match.Builder mb = sw.getOFFactory().buildMatch(); + OFFlowAdd.Builder fab = sw.getOFFactory().buildFlowAdd(); + + /* These are common to all flows for VLAN flow table. */ + fab.setBufferId(OFBufferId.NO_BUFFER) + .setCookie(COOKIE) + .setHardTimeout(HARD_TIMEOUT) + .setIdleTimeout(IDLE_TIMEOUT) + .setPriority(PRIORITY) + .setTableId(Tables.VLAN); + + for (OFPortModeTuple p : ports) { + /* If VLAN tag not present add a tag (internal=1 or defined) */ + if (vlan.equals(VlanVid.ZERO) || p.getMode() == OFPortMode.ACCESS) { /* push tag if access or untagged entirely */ + mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.UNTAGGED); + // happens automatically actions.add(sw.getOFFactory().actions().buildPushVlan().setEthertype(EthType.VLAN_FRAME).build()); + actions.add(sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))))); + instructions.add(sw.getOFFactory().instructions().applyActions(actions)); + } else { + mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan))); + } + + /* No matter what, we need to match on the ingress port */ + mb.setExact(MatchField.IN_PORT, p.getPort()); + + /* We have to do this for OF-DPA. It's like adding the VLAN to the switch on that port. */ + instructions.add(sw.getOFFactory().instructions().applyActions(Collections.singletonList((OFAction) + sw.getOFFactory().actions().setField(sw.getOFFactory().oxms().vlanVid(OFVlanVidMatch.ofVlanVid((vlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : vlan)))) + ))); + /* No matter what, output to the next table */ + instructions.add(sw.getOFFactory().instructions().gotoTable(Tables.TERMINATION_MAC)); + + /* Set the new stuff. */ + fab.setInstructions(instructions) + .setMatch(mb.build()) + .build(); + sw.write(fab.build()); + log.debug("Writing prereq flow to VLAN flow table {}", fab.build().toString()); + + /* Don't forget to empty out our containers for the next iteration (or below). */ + instructions.clear(); + actions.clear(); + mb = sw.getOFFactory().buildMatch(); + } + + /* + * We will insert a DLF flow to send to controller in the Policy ACL table (60). + * TODO Maybe this isn't the best choice, since we assume bypass/auto-forwarding of bridging and unicast/mulicast routing tables. + */ + actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xffFFffFF)); + instructions.add(sw.getOFFactory().instructions().applyActions(actions)); + fab = fab.setMatch(sw.getOFFactory().buildMatch().build()) /* clear match */ + .setInstructions(instructions) + .setPriority(DLF_PRIORITY) /* different zero priority and table ID here */ + .setTableId(Tables.POLICY_ACL); + sw.write(fab.build()); + log.debug("Writing DLF flow to policy ACL table {}", fab.build().toString()); + + return true; + } + + /** + * Note: Must have individually added {@link OFDPAUtils#addLearningSwitchPrereqGroups(IOFSwitch, VlanVid, List) groups} + * and then {@link OFDPAUtils#addLearningSwitchPrereqFlows(IOFSwitch, VlanVid, List) flows}, + * or must have done {@link OFDPAUtils#addLearningSwitchPrereqs(IOFSwitch, VlanVid, List) both} prior to calling + * this function. It is assumed you have done the aforementioned with the same VLAN and ports, otherwise you will likely + * get a very grumpy OF-DPA switch. + * + * Based on an intent described by a Match and an output OFPort, + * add a flow to an OF-DPA switch, conforming to the pipeline it + * exposes. The provided Match must contain at least the destination + * MAC address. If you would like to match on an untagged ethernet + * frame, omit MatchField.VLAN_VID from the Match object so that it + * is wildcarded. To the contrary, if you would like to match on a + * specific VLAN tag, please include this field. It is not possible + * to match on both tagged and untagged packets in a single flow. + * + * Implementation note: If a VLAN tag is not provided in the Match, the + * default VLAN of 1 will be assigned for internal use only; it will + * not appear on any packets egress the switch. + * + * If the packet is to be sent out on a different VLAN (e.g. for VLAN + * translation) then the VlanVid must be specified. + * + * An output OFPort must be specified. If the desired action is to + * flood, use OFPort.FLOOD; for sending to the controller, use + * OFPort.CONTROLLER; etc. + * + * @param s, must be an OF-DPA switch + * @param m, must contain at least the destination MAC + * @param outVlan, either a valid VLAN ID or ZERO for untagged + * @param outPort, either a valid physical port number or ZERO (for drop), ALL, FLOOD, or CONTROLLER + * @return true upon success; false if switch is not an OF-DPA switch + */ + public static boolean addBridgingFlow(IOFSwitch sw, U64 cookie, int priority, int hardTimeout, int idleTimeout, Match match, VlanVid outVlan, OFPort outPort) { + if (!isOFDPASwitch(sw)) { + log.error("Switch {} is not an OF-DPA switch. Not inserting flows.", sw.getId().toString()); + return false; + } + + /* + * Prepare and/or check arguments against requirements. + */ + cookie = (cookie == null ? U64.ZERO : cookie); + priority = (priority < 1 ? 1 : priority); + hardTimeout = (hardTimeout < 0 ? 0 : hardTimeout); + idleTimeout = (idleTimeout < 0 ? 0 : idleTimeout); + if (match == null || !match.isExact(MatchField.ETH_DST)) { + log.error("OF-DPA 2.0 requires at least the destination MAC be matched in order to forward through its pipeline."); + return false; + } else { + List<MatchFields> mfs = checkMatchFields(match); + if (mfs != null) { + log.error("OF-DPA 2.0 does not support matching on the following fields: {}", mfs.toString()); + return false; + } + } + outVlan = (outVlan == null ? VlanVid.ZERO : outVlan); + outPort = (outPort == null ? OFPort.ZERO : outPort); + + /* + * Ingress flow table (0) will automatically send to the + * VLAN flow table (10), so insert nothing here. + */ + + /* + * VLAN flow table (10) is handled by prereq flows. + */ + + /* + * Termination MAC table (20) will automatically send to the + * bridging flow table (50), so also insert nothing here. + * + * Can send to controller. + */ + + /* + * Unicast routing (30) and multicast routing (40) flow tables + * are special use-case tables the application should program + * directly. As such, we won't consider them here. + * + * Can send to controller. + */ + + /* + * Bridging table (50) should assign a write-action goto-group + * depending on the desired output action (single-port or + * flood). But, the default on miss is to go to the policy ACL + * table (60), which we will do. Policy ACL can also assign the group. + * which we will do. Bridging must match on the VLAN VID and the + * dest MAC address of the packet. It must have a priority greater + * than all less-specific flows in the table (i.e. wildcarded + * flows). We will reserve priority 0 for a DLF (destination + * lookup failure) flow, which would have all fields wildcarded. + * + * Can send to controller. + */ + + /* + * Policy ACL table (60) allows for more detailed matches. This + * is where we will implement all the matches specified in the Match + * object. The write-actions goto group inserted by the bridging + * table (50), or here (60), will be the output action taken upon + * a match, since this is the end of the pipeline. If we want to + * drop a packet for not matching, then no output group will be + * assigned to the packet, thus dropping it. + * + * A DLF (destination lookup failure) flow can also be inserted + * here to forward packets to the controller. + * + * Can send to controller. + */ + + ArrayList<OFInstruction> instructions = new ArrayList<OFInstruction>(); + ArrayList<OFAction> actions = new ArrayList<OFAction>(); + + /* Set the group to which we want to output. This might be a L2 flood or interface. */ + if (outPort.equals(OFPort.ZERO)) { + /* Don't add a group at all --> DROP */ + } else if (outPort.equals(OFPort.FLOOD) || outPort.equals(OFPort.ALL)) { // TODO how to distinguish in OF-DPA? + actions.add( + sw.getOFFactory().actions().group( // TODO Assume there is only one flood group per VLAN + GroupIds.createL2Flood(U16.ZERO, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan)) + ) + ); + instructions.add(sw.getOFFactory().instructions().writeActions(actions)); + } else if (outPort.equals(OFPort.CONTROLLER)) { + actions.add(sw.getOFFactory().actions().output(OFPort.CONTROLLER, 0xFFffFFff)); + instructions.add(sw.getOFFactory().instructions().applyActions(actions)); + } else { /* assume port is a number valid on the switch */ + actions.add( + sw.getOFFactory().actions().group( + GroupIds.createL2Interface(outPort, (outVlan.equals(VlanVid.ZERO) ? VlanVid.ofVlan(1) : outVlan)) + ) + ); + instructions.add(sw.getOFFactory().instructions().writeActions(actions)); + } + + /* We're allowed to match on anything in the Match (supplied as an argument to this function) at this point. */ + OFFlowAdd fa = sw.getOFFactory().buildFlowAdd() + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(cookie) + .setHardTimeout(hardTimeout) + .setIdleTimeout(idleTimeout) + .setPriority(priority) + .setTableId(Tables.POLICY_ACL) + .setMatch(match) + .setInstructions(instructions) + .build(); + sw.write(fa); + + return true; + } + + /** + * Generates group IDs according to the OF-DPA 2.0 spec. A create + * helper function is provided for each type of group, including + * sub-types, which should be used to set a valid group ID in any + * group mod sent to any OF-DPA switch. + */ + public static class GroupIds { + private GroupIds() {} + + public static OFGroup createL2Interface(OFPort p, VlanVid v) { //0 + return OFGroup.of(0 | p.getShortPortNumber() | (v.getVlan() << 16) | (OFDPAGroupType.L2_INTERFACE << 28)); + } + /** + * Only bits 0-27 of id are used. Bits 28-31 are ignored. + * @param id + * @return + */ + public static OFGroup createL2Rewrite(U32 id) { //1 + return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L2_REWRITE << 28)); + } + /** + * Only bits 0-27 of id are used. Bits 28-31 are ignored. + * @param id + * @return + */ + public static OFGroup createL3Unicast(U32 id) { //2 + return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_UNICAST << 28)); + } + public static OFGroup createL2Multicast(U16 id, VlanVid v) { //3 + return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L2_MULTICAST << 28)); + } + public static OFGroup createL2Flood(U16 id, VlanVid v) { //4 + return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L2_FLOOD << 28)); + } + /** + * Only bits 0-27 of id are used. Bits 28-31 are ignored. + * @param id + * @return + */ + public static OFGroup createL3Interface(U32 id) { //5 + return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_INTERFACE << 28)); + } + public static OFGroup createL3Multicast(U16 id, VlanVid v) { //6 + return OFGroup.of(0 | id.getRaw() | (v.getVlan() << 16) | (OFDPAGroupType.L3_MULTICAST << 28)); + } + /** + * Only bits 0-27 of id are used. Bits 28-31 are ignored. + * @param id + * @return + */ + public static OFGroup createL3ECMP(U32 id) { //7 + return OFGroup.of(0 | (id.getRaw() & 0x0FffFFff) | (OFDPAGroupType.L3_ECMP << 28)); + } + /** + * Only bits 0-9 of index are used. Bits 10-15 are ignored. + * @param index + * @param tunnelId + * @return + */ + public static OFGroup createL2DCOFloodOverUnicastTunnels(U16 index, U16 tunnelId) { //8 + return OFGroup.of(0 | (index.getRaw() & 0x03ff) + | (tunnelId.getRaw() << 12) + | (L2OverlaySubType.L2_OVERLAY_FLOOD_OVER_UNICAST_TUNNELS << 10) + | (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28)); + } + /** + * Only bits 0-9 of index are used. Bits 10-15 are ignored. + * @param index + * @param tunnelId + * @return + */ + public static OFGroup createL2DCOFloodOverMulticastTunnels(U16 index, U16 tunnelId) { //8 + return OFGroup.of(0 | (index.getRaw() & 0x03ff) + | (tunnelId.getRaw() << 12) + | (L2OverlaySubType.L2_OVERLAY_FLOOD_OVER_MULTICAST_TUNNELS << 10) + | (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28)); + } + /** + * Only bits 0-9 of index are used. Bits 10-15 are ignored. + * @param index + * @param tunnelId + * @return + */ + public static OFGroup createL2DCOMulticastOverUnicastTunnels(U16 index, U16 tunnelId) { //8 + return OFGroup.of(0 | (index.getRaw() & 0x03ff) + | (tunnelId.getRaw() << 12) + | (L2OverlaySubType.L2_OVERLAY_MULTICAST_OVER_UNICAST_TUNNELS << 10) + | (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28)); + } + /** + * Only bits 0-9 of index are used. Bits 10-15 are ignored. + * @param index + * @param tunnelId + * @return + */ + public static OFGroup createL2DCOMulticastOverMulticastTunnels(U16 index, U16 tunnelId) { //8 + return OFGroup.of(0 | (index.getRaw() & 0x03ff) + | (tunnelId.getRaw() << 12) + | (L2OverlaySubType.L2_OVERLAY_MULTICAST_OVER_MULTICAST_TUNNELS << 10) + | (OFDPAGroupType.L2_DATA_CENTER_OVERLAY << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSInterfaceLabel(U32 index) { //9 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_INTERFACE << 24) + | (OFDPAGroupType.MPLS_LABEL << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSL2VPNLabel(U32 index) { //9 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_L2_VPN_LABEL << 24) + | (OFDPAGroupType.MPLS_LABEL << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSL3VPNLabel(U32 index) { //9 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_L3_VPN_LABEL << 24) + | (OFDPAGroupType.MPLS_LABEL << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSTunnelLable1(U32 index) { //9 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_TUNNEL_LABEL_1 << 24) + | (OFDPAGroupType.MPLS_LABEL << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSTunnelLabel2(U32 index) { //9 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_TUNNEL_LABEL_2 << 24) + | (OFDPAGroupType.MPLS_LABEL << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSSwapLabel(U32 index) { //9 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_SWAP_LABEL << 24) + | (OFDPAGroupType.MPLS_LABEL << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSForwardingFastFailover(U32 index) { //10 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_FAST_FAILOVER << 24) + | (OFDPAGroupType.MPLS_FORWARDING << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSForwardingECMP(U32 index) { //10 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_ECMP << 24) + | (OFDPAGroupType.MPLS_FORWARDING << 28)); + } + /** + * Only bits 0-23 of index are used. Bits 24-31 are ignored. + * @param index + * @return + */ + public static OFGroup createMPLSForwardingL2Tag(U32 index) { //10 + return OFGroup.of(0 | (index.getRaw() & 0x00ffFFff) + | (MPLSSubType.MPLS_L2_TAG << 24) + | (OFDPAGroupType.MPLS_FORWARDING << 28)); + } + public static OFGroup createL2UnfilteredInterface(OFPort p) { //11 + return OFGroup.of(0 | p.getShortPortNumber() | (OFDPAGroupType.L2_UNFILTERED_INTERFACE << 28)); + } + public static OFGroup createL2Loopback(OFPort p) { //12 + return OFGroup.of(0 | p.getShortPortNumber() | (OFDPAGroupType.L2_LOOPBACK << 28)); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java b/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java index 9e2e676137741114cdc3fafac5a1c4d8383159a1..2914322d7834b945214858c13b675dcbda3fcfd2 100644 --- a/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java +++ b/src/main/java/net/floodlightcontroller/util/OFMessageUtils.java @@ -1,6 +1,19 @@ package net.floodlightcontroller.util; + +import java.util.ArrayList; +import java.util.List; + +import net.floodlightcontroller.core.IOFSwitch; + import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPacketIn; +import org.projectfloodlight.openflow.protocol.OFPacketOut; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.match.MatchField; +import org.projectfloodlight.openflow.types.OFBufferId; +import org.projectfloodlight.openflow.types.OFPort; /** * Tools to help work with OFMessages. @@ -37,4 +50,41 @@ public class OFMessageUtils { OFMessage.Builder mb = b.createBuilder().setXid(a.getXid()); return a.equals(mb.build()); } + + /** + * Writes an OFPacketOut message to a switch. + * + * @param sw + * The switch to write the PacketOut to. + * @param packetInMessage + * The corresponding PacketIn. + * @param egressPort + * The switchport to output the PacketOut. + */ + public static void writePacketOutForPacketIn(IOFSwitch sw, + OFPacketIn packetInMessage, OFPort egressPort) { + + OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut(); + + // Set buffer_id, in_port, actions_len + pob.setBufferId(packetInMessage.getBufferId()); + pob.setInPort(packetInMessage.getVersion().compareTo(OFVersion.OF_12) < 0 ? packetInMessage + .getInPort() : packetInMessage.getMatch().get( + MatchField.IN_PORT)); + + // set actions + List<OFAction> actions = new ArrayList<OFAction>(1); + actions.add(sw.getOFFactory().actions().buildOutput() + .setPort(egressPort).setMaxLen(0xffFFffFF).build()); + pob.setActions(actions); + + // set data - only if buffer_id == -1 + if (packetInMessage.getBufferId() == OFBufferId.NO_BUFFER) { + byte[] packetData = packetInMessage.getData(); + pob.setData(packetData); + } + + // and write it out + sw.write(pob.build()); + } } diff --git a/src/main/java/net/floodlightcontroller/util/OFPortMode.java b/src/main/java/net/floodlightcontroller/util/OFPortMode.java new file mode 100644 index 0000000000000000000000000000000000000000..9a02a47fb93a3f54c5960ae5af93490d2a85d2bc --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/OFPortMode.java @@ -0,0 +1,14 @@ +package net.floodlightcontroller.util; + +/** + * Define the different operating modes of a switch + * port as trunk, access, and both (trunk and access). + * + * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu + */ + +public enum OFPortMode { + TRUNK, + ACCESS, + TRUNK_AND_ACCESS +} diff --git a/src/main/java/net/floodlightcontroller/util/OFPortModeTuple.java b/src/main/java/net/floodlightcontroller/util/OFPortModeTuple.java new file mode 100644 index 0000000000000000000000000000000000000000..0b3fd9cb22f9a77e9a1664b561efeffab4c53c58 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/util/OFPortModeTuple.java @@ -0,0 +1,72 @@ +package net.floodlightcontroller.util; + +import javax.annotation.Nonnull; + +import org.projectfloodlight.openflow.types.OFPort; + +/** + * Make it easier to track a port and it's operating {@link OFPortMode mode}. + * Objects of this class are immutable. + * + * @author Ryan Izard, ryan.izard@bigswitch.com, rizard@g.clemson.edu + */ +public class OFPortModeTuple { + private final OFPort p; + private final OFPortMode m; + + private OFPortModeTuple(@Nonnull OFPort p, @Nonnull OFPortMode m) { + this.p = p; + this.m = m; + } + + public static OFPortModeTuple of(OFPort p, OFPortMode m) { + if (p == null) { + throw new NullPointerException("Port cannot be null."); + } + if (m == null) { + throw new NullPointerException("Mode cannot be null."); + } + return new OFPortModeTuple(p, m); + } + + public OFPort getPort() { + return this.p; + } + + public OFPortMode getMode() { + return this.m; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((m == null) ? 0 : m.hashCode()); + result = prime * result + ((p == null) ? 0 : p.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + OFPortModeTuple other = (OFPortModeTuple) obj; + if (m != other.m) + return false; + if (p == null) { + if (other.p != null) + return false; + } else if (!p.equals(other.p)) + return false; + return true; + } + + @Override + public String toString() { + return "OFPortModeTuple [p=" + p + ", m=" + m + "]"; + } +} diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java index f5ea145c4af628df9331907d7ca7ca5942969f34..cab875e333bdc047514e2ed8568fb182f1aec605 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java @@ -520,6 +520,12 @@ implements IFloodlightModule, IVirtualNetworkService, IOFMessageListener { // add or remove entry as gateway deviceAdded(device); } + + @Override + public void deviceIPV6AddrChanged(IDevice device) { + //TODO + log.debug("IPv6 address change not handled in VirtualNetworkFilter. Device: {}", device.toString()); + } @Override public void deviceMoved(IDevice device) { diff --git a/src/main/resources/floodlightdefault.properties b/src/main/resources/floodlightdefault.properties index 13ce05be948ee7b2cc60665f69491cb92fbc4f18..8c644c730d806b8140cd24cd19409d23d4ccc00c 100644 --- a/src/main/resources/floodlightdefault.properties +++ b/src/main/resources/floodlightdefault.properties @@ -23,12 +23,14 @@ org.sdnplatform.sync.internal.SyncManager.port=6642 net.floodlightcontroller.forwarding.Forwarding.match=vlan, mac, ip, transport net.floodlightcontroller.core.internal.FloodlightProvider.openflowPort=6653 net.floodlightcontroller.core.internal.FloodlightProvider.role=ACTIVE -net.floodlightcontroller.core.internal.OFSwitchManager.addDefaultSendToControllerFlowInTables='{"all":"0,1,2"}' +net.floodlightcontroller.core.internal.OFSwitchManager.defaultMaxTablesToReceiveTableMissFlow=2 +net.floodlightcontroller.core.internal.OFSwitchManager.maxTablesToReceiveTableMissFlowPerDpid={"00:00:00:00:00:00:00:01":"1","2":"1"} net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnInitialHandshakeAsMaster=YES net.floodlightcontroller.core.internal.OFSwitchManager.clearTablesOnEachTransitionToMaster=YES net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePath=/path/to/your/keystore-file.jks net.floodlightcontroller.core.internal.OFSwitchManager.keyStorePassword=your-keystore-password net.floodlightcontroller.core.internal.OFSwitchManager.useSsl=NO +net.floodlightcontroller.core.internal.OFSwitchManager.supportedOpenFlowVersions=1.0, 1.1, 1.2, 1.3, 1.4 net.floodlightcontroller.restserver.RestApiServer.keyStorePath=/path/to/your/keystore-file.jks net.floodlightcontroller.restserver.RestApiServer.keyStorePassword=your-keystore-password net.floodlightcontroller.restserver.RestApiServer.httpsNeedClientAuthentication=NO diff --git a/src/main/resources/logback-test.xml b/src/main/resources/logback-test.xml index 2c8ca28ccbefd41c21583decb480458dc076366a..21c48f39c6a6b94e501b3bafd9a97528b25f2b02 100644 --- a/src/main/resources/logback-test.xml +++ b/src/main/resources/logback-test.xml @@ -17,7 +17,6 @@ <logger name="net.floodlightcontroller.devicemanager" level="INFO"></logger> <logger name="net.floodlightcontroller.packet" level="INFO"></logger> <logger name="net.floodlightcontroller.forwarding" level="INFO"></logger> - <logger name="net.floodlightcontroller.routing" level="INFO"></logger> <logger name="net.floodlightcontroller.core.internal" level="INFO"></logger> <logger level="INFO" name="net.floodlightcontroller.learningswitch"></logger> <logger level="INFO" name="org.projectfloodlight.openflow"></logger> diff --git a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java index 230f1e93436e498cfafd4789eb0811c80fce1abc..f9c4d0487f11a7eca2dfeeca41f005757c15b7b4 100644 --- a/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java +++ b/src/test/java/net/floodlightcontroller/accesscontrollist/ACLTest.java @@ -51,8 +51,6 @@ import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IEntityClassifierService; import net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier; import net.floodlightcontroller.devicemanager.test.MockDeviceManager; -import net.floodlightcontroller.packet.Ethernet; -import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.restserver.RestApiServer; import net.floodlightcontroller.staticflowentry.StaticFlowEntryPusher; @@ -69,7 +67,11 @@ import org.junit.Test; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.VlanVid; public class ACLTest extends FloodlightTestCase { @@ -177,14 +179,12 @@ public class ACLTest extends FloodlightTestCase { Map<String, Object> row; // a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")), - null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"), + VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); // a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")), - null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"), + VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1)); // rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32) rule1 = new ACLRule(); @@ -344,9 +344,8 @@ public class ACLTest extends FloodlightTestCase { assertEquals(acl.getRules().size(), 1); // a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")), - null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"), + VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); resultSet = storageService.getRow( StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01"); @@ -364,9 +363,8 @@ public class ACLTest extends FloodlightTestCase { } // a new AP[dpid:00:00:00:00:00:00:00:01 port:2 ip:10.0.0.2] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:02")), - null, IPv4.toIPv4Address("10.0.0.2"), 1L, 2); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:02"), + VlanVid.ZERO, IPv4Address.of("10.0.0.2"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(2)); resultSet = storageService.getRow( StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01"); @@ -392,9 +390,8 @@ public class ACLTest extends FloodlightTestCase { assertEquals(acl.getRules().size(), 2); // a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")), - null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"), + VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1)); resultSet = storageService.getRow( StaticFlowEntryPusher.TABLE_NAME, "ACLRule_2_00:00:00:00:00:00:00:02"); @@ -428,9 +425,8 @@ public class ACLTest extends FloodlightTestCase { Map<String, Object> row; // a new AP[dpid:00:00:00:00:00:00:00:01 port:1] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")), - null, null, 1L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"), + VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); // rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32) rule1 = new ACLRule(); @@ -455,9 +451,8 @@ public class ACLTest extends FloodlightTestCase { assertEquals(it.hasNext(), false); // a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")), - null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"), + VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); resultSet = storageService.getRow( StaticFlowEntryPusher.TABLE_NAME, "ACLRule_1_00:00:00:00:00:00:00:01"); @@ -491,14 +486,12 @@ public class ACLTest extends FloodlightTestCase { Map<String, Object> row; // a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")), - null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"), + VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); // a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")), - null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"), + VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1)); // rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32) rule1 = new ACLRule(); @@ -579,14 +572,12 @@ public class ACLTest extends FloodlightTestCase { Map<String, Object> row; // a new AP[dpid:00:00:00:00:00:00:00:01 port:1 ip:10.0.0.1] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:01")), - null, IPv4.toIPv4Address("10.0.0.1"), 1L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:01"), + VlanVid.ZERO, IPv4Address.of("10.0.0.1"), IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); // a new AP[dpid:00:00:00:00:00:00:00:02 port:1 ip:10.0.0.3] appears - deviceManager.learnEntity( - Ethernet.toLong(Ethernet.toMACAddress("00:00:00:00:00:03")), - null, IPv4.toIPv4Address("10.0.0.3"), 2L, 1); + deviceManager.learnEntity(MacAddress.of("00:00:00:00:00:03"), + VlanVid.ZERO, IPv4Address.of("10.0.0.3"), IPv6Address.NONE, DatapathId.of(2), OFPort.of(1)); // rule1 indicates host(10.0.0.0/28) can not access TCP port 80 in host(10.0.0.254/32) rule1 = new ACLRule(); diff --git a/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java b/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java index fc9a0c3c3b0a7c4320eafca78e601ae1d8205495..24db287af6da5c28214280f1a240991dafc27e55 100644 --- a/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java +++ b/src/test/java/net/floodlightcontroller/core/OFSwitchBaseTest.java @@ -154,9 +154,9 @@ public class OFSwitchBaseTest { switches.put(sw.getId(), sw); reset(switchManager); //expect(switchManager.getSwitch(sw.getId())).andReturn(sw).anyTimes(); + setUpPorts(); } - @Before public void setUpPorts() { OFPortDesc.Builder pdb = OFFactories.getFactory(OFVersion.OF_13).buildPortDesc(); // p1a is disabled @@ -1394,6 +1394,8 @@ public class OFSwitchBaseTest { reset(switchManager); expect(switchManager.isCategoryRegistered(category)).andReturn(true); + switchManager.handleOutgoingMessage(sw, testMessage); + expectLastCall(); replay(switchManager); sw.write(testMessage, category); diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index 7e0d2ceac1532ffff217e8990c49d7806b38cdbb..669876e7904ba16f5db21284869fc387724be6d3 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -46,7 +46,6 @@ import net.floodlightcontroller.debugevent.IDebugEventService; import net.floodlightcontroller.packet.ARP; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPacket; -import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.perfmon.IPktInProcessingTimeService; import net.floodlightcontroller.perfmon.PktInProcessingTime; import net.floodlightcontroller.restserver.IRestApiService; @@ -74,6 +73,8 @@ import org.projectfloodlight.openflow.protocol.OFPacketInReason; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; +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.protocol.OFPortDesc; @@ -180,10 +181,10 @@ public class ControllerTest extends FloodlightTestCase { .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) - .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00")) - .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); + .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:00")) + .setSenderProtocolAddress(IPv4Address.of("192.168.1.1")) + .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55")) + .setTargetProtocolAddress(IPv4Address.of("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); // The specific factory can be obtained from the switch, but we don't have one @@ -243,7 +244,7 @@ public class ControllerTest extends FloodlightTestCase { expect(sw.getSwitchDescription()).andReturn(description).atLeastOnce(); expect(sw.getBuffers()) .andReturn(featuresReply.getNBuffers()).atLeastOnce(); - expect(sw.getTables()) + expect(sw.getNumTables()) .andReturn(featuresReply.getNTables()).atLeastOnce(); expect(sw.getCapabilities()) .andReturn(featuresReply.getCapabilities()).atLeastOnce(); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java index 038639f79ff45cd566c5f92c3c684d377cb2f49d..96147b02eb52b4323be4f804ffad6014c552809b 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer10Test.java @@ -35,12 +35,14 @@ import org.jboss.netty.util.Timer; import org.junit.After; import org.junit.Before; import org.junit.Test; + import net.floodlightcontroller.core.IOFConnectionBackend; import net.floodlightcontroller.core.OFConnectionCounters; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout; import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl; import net.floodlightcontroller.debugcounter.IDebugCounterService; + import org.projectfloodlight.openflow.protocol.OFActionType; import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFCapabilities; @@ -63,6 +65,7 @@ import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.U32; import com.google.common.collect.ImmutableList; @@ -92,7 +95,6 @@ public class OFChannelHandlerVer10Test { private Capture<IOFConnectionBackend> newConnection; private Capture<OFFeaturesReply> newFeaturesReply; - @Before public void setUpFeaturesReply() { portDesc = factory.buildPortDesc() .setName("Eth1") @@ -111,6 +113,7 @@ public class OFChannelHandlerVer10Test { @Before public void setUp() throws Exception { + setUpFeaturesReply(); switchManager = createMock(IOFSwitchManager.class); connectionListener = createMock(IOFConnectionListener.class); newConnectionListener = createMock(INewOFConnectionListener.class); @@ -137,7 +140,7 @@ public class OFChannelHandlerVer10Test { replay(switchManager); handler = new OFChannelHandler(switchManager, newConnectionListener, pipeline, debugCounterService, - timer); + timer, Collections.singletonList(U32.of(0)), OFFactories.getFactory(OFVersion.OF_14)); verify(switchManager); reset(switchManager); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java index 0703ef8c49a8ecdd052c59c96f176bf16a516ab8..9b9b6ce897bf00c210326fed7d49e315500e11eb 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFChannelHandlerVer13Test.java @@ -35,12 +35,14 @@ import org.jboss.netty.util.Timer; import org.junit.After; import org.junit.Before; import org.junit.Test; + import net.floodlightcontroller.core.IOFConnectionBackend; import net.floodlightcontroller.core.OFConnectionCounters; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandler; import net.floodlightcontroller.core.internal.OpenflowPipelineFactory.PipelineHandshakeTimeout; import net.floodlightcontroller.debugcounter.DebugCounterServiceImpl; import net.floodlightcontroller.debugcounter.IDebugCounterService; + import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFBsnSetAuxCxnsReply; import org.projectfloodlight.openflow.protocol.OFCapabilities; @@ -64,6 +66,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.U32; import com.google.common.collect.ImmutableList; @@ -96,7 +99,6 @@ public class OFChannelHandlerVer13Test { private Capture<OFFeaturesReply> newFeaturesReply; - @Before public void setUpFeaturesReply() { portDesc = factory.buildPortDesc() .setName("Eth1") @@ -113,6 +115,7 @@ public class OFChannelHandlerVer13Test { @Before public void setUp() throws Exception { + setUpFeaturesReply(); switchManager = createMock(IOFSwitchManager.class); connectionListener = createMock(IOFConnectionListener.class); newConnectionListener = createMock(INewOFConnectionListener.class); @@ -139,7 +142,9 @@ public class OFChannelHandlerVer13Test { expect(switchManager.getCounters()).andReturn(counters).anyTimes(); replay(switchManager); handler = new OFChannelHandler(switchManager, newConnectionListener, - pipeline, debugCounterService, timer); + pipeline, debugCounterService, /* 62 is OF versions 1.0 thru 1.4 in decimal */ + timer, Collections.singletonList(U32.of(62)), OFFactories.getFactory(OFVersion.OF_14)); + verify(switchManager); reset(switchManager); diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java index e7590df1c838b059a9bdfc33f4387bfc8aecc66b..675873a5eeac46c7e8db6cef30e31ea02680fd08 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchHandlerTestBase.java @@ -48,6 +48,7 @@ import net.floodlightcontroller.debugcounter.IDebugCounterService; import org.projectfloodlight.openflow.protocol.OFBadActionCode; import org.projectfloodlight.openflow.protocol.OFBadRequestCode; +import org.projectfloodlight.openflow.protocol.OFBarrierReply; import org.projectfloodlight.openflow.protocol.OFControllerRole; import org.projectfloodlight.openflow.protocol.OFDescStatsReply; import org.projectfloodlight.openflow.protocol.OFErrorMsg; @@ -66,6 +67,7 @@ import org.projectfloodlight.openflow.protocol.OFStatsType; import org.projectfloodlight.openflow.protocol.OFTableFeatureProp; import org.projectfloodlight.openflow.protocol.OFTableFeaturesStatsReply; import org.projectfloodlight.openflow.protocol.OFType; +import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.OFAuxId; @@ -81,1085 +83,1119 @@ import com.google.common.collect.ImmutableList; public abstract class OFSwitchHandlerTestBase { - protected static final DatapathId dpid = DatapathId.of(0x42L); - - protected IOFSwitchManager switchManager; - protected RoleManager roleManager; - - private IDebugCounterService debugCounterService; - protected OFSwitchHandshakeHandler switchHandler; - protected MockOFConnection connection; - // Use a 1.0 factory for the 1.0 test - protected final OFFactory factory = getFactory(); - - protected OFFeaturesReply featuresReply; - protected List<IAppHandshakePluginFactory> plugins; - - private HashSet<Long> seenXids = null; - protected IOFSwitchBackend sw; - private Timer timer; - private TestHandshakePlugin handshakePlugin; - - private class TestHandshakePlugin extends OFSwitchAppHandshakePlugin { - protected TestHandshakePlugin(PluginResult defaultResult, int timeoutS) { - super(defaultResult, timeoutS); - } - - @Override - protected void processOFMessage(OFMessage m) { - } - - @Override - protected void enterPlugin() { - } - } - - @Before - public void setUpFeaturesReply() { - getFeaturesReply(); - this.featuresReply = getFeaturesReply(); - - // Plugin set - IAppHandshakePluginFactory factory = createMock(IAppHandshakePluginFactory.class); - PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine"); - handshakePlugin = new TestHandshakePlugin(result, 5); - expect(factory.createPlugin()).andReturn(handshakePlugin).anyTimes(); - replay(factory); - plugins = ImmutableList.of(factory); - } - - - @Before - public void setUp() throws Exception { - switchManager = createMock(IOFSwitchManager.class); - roleManager = createMock(RoleManager.class); - sw = createMock(IOFSwitchBackend.class); - timer = createMock(Timer.class); - expect(timer.newTimeout(anyObject(TimerTask.class), anyLong(), anyObject(TimeUnit.class))).andReturn(EasyMock.createNiceMock(Timeout.class)); - replay(timer); - seenXids = null; - - // TODO: should mock IDebugCounterService and make sure - // the expected counters are updated. - debugCounterService = new DebugCounterServiceImpl(); - SwitchManagerCounters counters = - new SwitchManagerCounters(debugCounterService); - expect(switchManager.getCounters()).andReturn(counters).anyTimes(); - replay(switchManager); - connection = new MockOFConnection(featuresReply.getDatapathId(), OFAuxId.MAIN); - switchHandler = new OFSwitchHandshakeHandler(connection, featuresReply, switchManager, roleManager, timer); - - // replay sw. Reset it if you need more specific behavior - replay(sw); - } - - - @After - public void tearDown() { - verifyAll(); - } - - private void verifyAll() { - assertThat("Unexpected messages have been captured", - connection.getMessages(), - Matchers.empty()); - // verify all mocks. - verify(sw); - } - - void verifyUniqueXids(OFMessage... msgs) { - verifyUniqueXids(Arrays.asList(msgs)); - } - - /** make sure that the transaction ids in the given messages are - * not 0 and differ between each other. - * While it's not a defect per se if the xids are we want to ensure - * we use different ones for each message we send. - */ - void verifyUniqueXids(List<OFMessage> msgs) { - if (seenXids == null) - seenXids = new HashSet<Long>(); - for (OFMessage m: msgs) { - long xid = m.getXid(); - assertTrue("Xid in messags is 0", xid != 0); - assertFalse("Xid " + xid + " has already been used", - seenXids.contains(xid)); - seenXids.add(xid); - } - } - - - /*************************** abstract phases / utilities to be filled in by the subclasses */ - - // Factory + messages - - /** @return the version-appropriate factory */ - public abstract OFFactory getFactory(); - - /** - * @return a version appropriate features reply (different in 1.3 because it - * doesn't have ports) - */ - abstract OFFeaturesReply getFeaturesReply(); - /** @return the class that's used for role requests/replies (OFNiciraRoleRequest vs. - * OFRequest) - */ - - /// Role differences - - abstract Class<?> getRoleRequestClass(); - /** Verify that the given OFMessage is a correct RoleRequest message - * for the given role using the given xid (for the version). - */ - public abstract void verifyRoleRequest(OFMessage m, - OFControllerRole expectedRole); - /** Return a RoleReply message for the given role */ - protected abstract OFMessage getRoleReply(long xid, OFControllerRole role); - - /// Difference in the handshake sequence - - /** OF1.3 has the PortDescStatsRequest, OF1.0 not */ - abstract void moveToPreConfigReply() throws Exception; - /** - * Move the channel from scratch to WaitAppHandshakeState - * Different for OF1.0 and OF1.3 because of GenTables. - * @throws Exception - */ - @Test - public abstract void moveToWaitAppHandshakeState() throws Exception; - - /** - * Move the channel from scratch to WaitSwitchDriverSubHandshake - * Different for OF1.0 and OF1.3 because of GenTables. - * @throws Exception - */ - @Test - public abstract void moveToWaitSwitchDriverSubHandshake() throws Exception; - - /** - * Move the channel from scratch to WaitInitialRole - * Different for OF1.0 and OF1.3 because of Controller Connections. - * @throws Exception - */ - @Test - public abstract void moveToWaitInitialRole() throws Exception; - - /*******************************************************************************************/ - - - /** Move the channel from scratch to INIT state + protected static final DatapathId dpid = DatapathId.of(0x42L); + + protected IOFSwitchManager switchManager; + protected RoleManager roleManager; + + private IDebugCounterService debugCounterService; + protected OFSwitchHandshakeHandler switchHandler; + protected MockOFConnection connection; + // Use a 1.0 factory for the 1.0 test + protected final OFFactory factory = getFactory(); + + protected OFFeaturesReply featuresReply; + protected List<IAppHandshakePluginFactory> plugins; + + private HashSet<Long> seenXids = null; + protected IOFSwitchBackend sw; + private Timer timer; + private TestHandshakePlugin handshakePlugin; + + private class TestHandshakePlugin extends OFSwitchAppHandshakePlugin { + protected TestHandshakePlugin(PluginResult defaultResult, int timeoutS) { + super(defaultResult, timeoutS); + } + + @Override + protected void processOFMessage(OFMessage m) { + } + + @Override + protected void enterPlugin() { + } + } + + + public void setUpFeaturesReply() { + getFeaturesReply(); + this.featuresReply = getFeaturesReply(); + + // Plugin set + IAppHandshakePluginFactory factory = createMock(IAppHandshakePluginFactory.class); + PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine"); + handshakePlugin = new TestHandshakePlugin(result, 5); + expect(factory.createPlugin()).andReturn(handshakePlugin).anyTimes(); + replay(factory); + plugins = ImmutableList.of(factory); + } + + + @Before + public void setUp() throws Exception { + /* + * This needs to be called explicitly to ensure the featuresReply is not null. + * Otherwise, there is no guarantee @Before will for setUpFeaturesReply() will + * call that function before our @Before setUp() here. + */ + setUpFeaturesReply(); + switchManager = createMock(IOFSwitchManager.class); + roleManager = createMock(RoleManager.class); + sw = createMock(IOFSwitchBackend.class); + timer = createMock(Timer.class); + expect(timer.newTimeout(anyObject(TimerTask.class), anyLong(), anyObject(TimeUnit.class))).andReturn(EasyMock.createNiceMock(Timeout.class)); + replay(timer); + seenXids = null; + + // TODO: should mock IDebugCounterService and make sure + // the expected counters are updated. + debugCounterService = new DebugCounterServiceImpl(); + SwitchManagerCounters counters = + new SwitchManagerCounters(debugCounterService); + expect(switchManager.getCounters()).andReturn(counters).anyTimes(); + replay(switchManager); + connection = new MockOFConnection(featuresReply.getDatapathId(), OFAuxId.MAIN); + switchHandler = new OFSwitchHandshakeHandler(connection, featuresReply, switchManager, roleManager, timer); + + // replay sw. Reset it if you need more specific behavior + replay(sw); + } + + + @After + public void tearDown() { + verifyAll(); + } + + private void verifyAll() { + assertThat("Unexpected messages have been captured", + connection.getMessages(), + Matchers.empty()); + // verify all mocks. + verify(sw); + } + + void verifyUniqueXids(OFMessage... msgs) { + verifyUniqueXids(Arrays.asList(msgs)); + } + + /** make sure that the transaction ids in the given messages are + * not 0 and differ between each other. + * While it's not a defect per se if the xids are we want to ensure + * we use different ones for each message we send. + */ + void verifyUniqueXids(List<OFMessage> msgs) { + if (seenXids == null) + seenXids = new HashSet<Long>(); + for (OFMessage m: msgs) { + long xid = m.getXid(); + assertTrue("Xid in messags is 0", xid != 0); + assertFalse("Xid " + xid + " has already been used", + seenXids.contains(xid)); + seenXids.add(xid); + } + } + + + /*************************** abstract phases / utilities to be filled in by the subclasses */ + + // Factory + messages + + /** @return the version-appropriate factory */ + public abstract OFFactory getFactory(); + + /** + * @return a version appropriate features reply (different in 1.3 because it + * doesn't have ports) + */ + abstract OFFeaturesReply getFeaturesReply(); + /** @return the class that's used for role requests/replies (OFNiciraRoleRequest vs. + * OFRequest) + */ + + /// Role differences + + abstract Class<?> getRoleRequestClass(); + /** Verify that the given OFMessage is a correct RoleRequest message + * for the given role using the given xid (for the version). + */ + public abstract void verifyRoleRequest(OFMessage m, + OFControllerRole expectedRole); + /** Return a RoleReply message for the given role */ + protected abstract OFMessage getRoleReply(long xid, OFControllerRole role); + + /// Difference in the handshake sequence + + /** OF1.3 has the PortDescStatsRequest, OF1.0 not */ + abstract void moveToPreConfigReply() throws Exception; + /** + * Move the channel from scratch to WaitAppHandshakeState + * Different for OF1.0 and OF1.3 because of GenTables. + * @throws Exception + */ + @Test + public abstract void moveToWaitAppHandshakeState() throws Exception; + + /** + * Move the channel from scratch to WaitSwitchDriverSubHandshake + * Different for OF1.0 and OF1.3 because of GenTables. + * @throws Exception + */ + @Test + public abstract void moveToWaitSwitchDriverSubHandshake() throws Exception; + + /** + * Move the channel from scratch to WaitInitialRole + * Different for OF1.0 and OF1.3 because of Controller Connections. + * @throws Exception + */ + @Test + public abstract void moveToWaitInitialRole() throws Exception; + + /*******************************************************************************************/ + + + /** Move the channel from scratch to INIT state This occurs upon creation of the switch handler - */ - @Test - public void testInitState() throws Exception { - assertThat(connection.getListener(), notNullValue()); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.InitState.class)); - } - - - /** Move the channel from scratch to WAIT_CONFIG_REPLY state - * adds testing for beginHandshake() which moves the state from - * InitState to WaitConfigReply. - */ - @Test - public void moveToWaitConfigReply() throws Exception { - moveToPreConfigReply(); - - List<OFMessage> msgs = connection.getMessages(); - assertEquals(3, msgs.size()); - assertEquals(OFType.SET_CONFIG, msgs.get(0).getType()); - OFSetConfig sc = (OFSetConfig)msgs.get(0); - assertEquals(0xffff, sc.getMissSendLen()); - assertEquals(OFType.BARRIER_REQUEST, msgs.get(1).getType()); - assertEquals(OFType.GET_CONFIG_REQUEST, msgs.get(2).getType()); - verifyUniqueXids(msgs); - msgs.clear(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitConfigReplyState.class)); - verifyAll(); - } - - - - /** Move the channel from scratch to WAIT_DESCRIPTION_STAT_REPLY state - * Builds on moveToWaitConfigReply() - * adds testing for WAIT_CONFIG_REPLY state - */ - @Test - public void moveToWaitDescriptionStatReply() throws Exception { - moveToWaitConfigReply(); - - connection.clearMessages(); - OFGetConfigReply cr = factory.buildGetConfigReply() - .setMissSendLen(0xFFFF) - .build(); - - switchHandler.processOFMessage(cr); - - OFMessage msg = connection.retrieveMessage(); - assertEquals(OFType.STATS_REQUEST, msg.getType()); - OFStatsRequest<?> sr = (OFStatsRequest<?>)msg; - assertEquals(OFStatsType.DESC, sr.getStatsType()); - verifyUniqueXids(msg); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitDescriptionStatReplyState.class)); - } - - protected OFDescStatsReply createDescriptionStatsReply() { - OFDescStatsReply statsReply = factory.buildDescStatsReply() - .setDpDesc("Datapath Description") - .setHwDesc("Hardware Description") - .setMfrDesc("Manufacturer Description") - .setSwDesc("Software Description") - .setSerialNum("Serial Number") - .build(); - return statsReply; - } - - protected OFTableFeaturesStatsReply createTableFeaturesStatsReply() { - OFTableFeaturesStatsReply statsReply = factory.buildTableFeaturesStatsReply() - .setEntries(Collections.singletonList(factory.buildTableFeatures() - .setConfig(0) - .setMaxEntries(100) - .setMetadataMatch(U64.NO_MASK) - .setMetadataWrite(U64.NO_MASK) - .setName("MyTable") - .setTableId(TableId.of(1)) - .setProperties(Collections.singletonList((OFTableFeatureProp)factory.buildTableFeaturePropMatch() - .setOxmIds(Collections.singletonList(U32.of(100))) - .build()) - ).build() - ) - - ).build(); - return statsReply; - } - - /** - * setup the expectations for the mock switch that are needed - * after the switch is instantiated in the WAIT_DESCRIPTION_STATS STATE - * Will reset the switch - * @throws CounterException - */ - protected void setupSwitchForInstantiationWithReset() - throws Exception { - reset(sw); - sw.setFeaturesReply(featuresReply); - expectLastCall().once(); - } - - /** - * Tests a situation where a switch returns a QUARANTINE result. This means - * we should move the handshake handler to a quarantine state and also - * quarantine the switch in the controller. - * - * @throws Exception - */ - @Test - public void moveQuarantine() throws Exception { - moveToWaitAppHandshakeState(); - - reset(switchManager); - switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.QUARANTINED); - expectLastCall().once(); - replay(switchManager); - - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(WaitAppHandshakeState.class)); - WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting(); - assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin)); - - reset(sw); - expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE); - sw.setStatus(SwitchStatus.QUARANTINED); - expectLastCall().once(); - replay(sw); - - PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine"); - handshakePlugin.exitPlugin(result); - - assertThat(switchHandler.getStateForTesting(), - CoreMatchers.instanceOf(QuarantineState.class)); - verify(switchManager); - } - - /** - * Tests a situation where a plugin returns a DISCONNECT result. This means - * we should disconnect the connection and the state should not change. - * - * @throws Exception - */ - @Test - public void failedAppHandshake() throws Exception { - moveToWaitAppHandshakeState(); - - assertThat(switchHandler.getStateForTesting(), - CoreMatchers.instanceOf(WaitAppHandshakeState.class)); - - WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting(); - assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin)); - - PluginResult result = new PluginResult(PluginResultType.DISCONNECT); - handshakePlugin.exitPlugin(result); - - assertThat(connection.isConnected(), equalTo(false)); - } - - - @Test - public void validAppHandshakePluginReason() throws Exception { - try{ - new PluginResult(PluginResultType.QUARANTINE,"This should not cause an exception"); - }catch(IllegalStateException e) { - fail("This should cause an illegal state exception"); - } - } - - @Test - public void invalidAppHandshakePluginReason() throws Exception { - try{ - new PluginResult(PluginResultType.CONTINUE,"This should cause an exception"); - fail("This should cause an illegal state exception"); - }catch(IllegalStateException e) { /* Expected */ } - - try{ - new PluginResult(PluginResultType.DISCONNECT,"This should cause an exception"); - fail("This should cause an illegal state exception"); - }catch(IllegalStateException e) { /* Expected */ } - } - - /** - * Move the channel from scratch to WAIT_INITIAL_ROLE state via - * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE - * Does extensive testing for the WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state - * - */ - @Test - public void testSwitchDriverSubHandshake() - throws Exception { - moveToWaitSwitchDriverSubHandshake(); - - //------------------------------------------------- - //------------------------------------------------- - // Send a message to the handler, it should be passed to the - // switch's sub-handshake handling. After this message the - // sub-handshake will be complete - // FIXME:LOJI: With Andi's fix for a default Match object we won't - // need to build/set this match object - - Match match = factory.buildMatch().build(); - OFMessage m = factory.buildFlowRemoved().setMatch(match).build(); - resetToStrict(sw); - sw.processDriverHandshakeMessage(m); - expectLastCall().once(); - expect(sw.isDriverHandshakeComplete()).andReturn(true).once(); - replay(sw); - - switchHandler.processOFMessage(m); - - assertThat(switchHandler.getStateForTesting(), - CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitAppHandshakeState.class)); - assertThat("Unexpected message captured", connection.getMessages(), Matchers.empty()); - verify(sw); - } - - @Test - /** Test WaitDescriptionReplyState */ - public void testWaitDescriptionReplyState() throws Exception { - moveToWaitInitialRole(); - } - - /** - * Setup the mock switch and write capture for a role request, set the - * role and verify mocks. - * @param supportsNxRole whether the switch supports role request messages - * to setup the attribute. This must be null (don't yet know if roles - * supported: send to check) or true. - * @param role The role to send - * @throws IOException - */ - private long setupSwitchSendRoleRequestAndVerify(Boolean supportsNxRole, - OFControllerRole role) throws IOException { - assertTrue("This internal test helper method most not be called " + - "with supportsNxRole==false. Test setup broken", - supportsNxRole == null || supportsNxRole == true); - reset(sw); - expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) - .andReturn(supportsNxRole).atLeastOnce(); - replay(sw); - - switchHandler.sendRoleRequest(role); - - OFMessage msg = connection.retrieveMessage(); - verifyRoleRequest(msg, role); - verify(sw); - return msg.getXid(); - } - - - /** - * Setup the mock switch for a role change request where the switch - * does not support roles. - * - * Needs to verify and reset the controller since we need to set - * an expectation - */ - @SuppressWarnings("unchecked") + */ + @Test + public void testInitState() throws Exception { + assertThat(connection.getListener(), notNullValue()); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.InitState.class)); + } + + + /** Move the channel from scratch to WAIT_CONFIG_REPLY state + * adds testing for beginHandshake() which moves the state from + * InitState to WaitConfigReply. + */ + @Test + public void moveToWaitConfigReply() throws Exception { + moveToPreConfigReply(); + + List<OFMessage> msgs = connection.getMessages(); + assertEquals(3, msgs.size()); + assertEquals(OFType.SET_CONFIG, msgs.get(0).getType()); + OFSetConfig sc = (OFSetConfig)msgs.get(0); + assertEquals(0xffff, sc.getMissSendLen()); + assertEquals(OFType.BARRIER_REQUEST, msgs.get(1).getType()); + assertEquals(OFType.GET_CONFIG_REQUEST, msgs.get(2).getType()); + verifyUniqueXids(msgs); + msgs.clear(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitConfigReplyState.class)); + verifyAll(); + } + + + + /** Move the channel from scratch to WAIT_DESCRIPTION_STAT_REPLY state + * Builds on moveToWaitConfigReply() + * adds testing for WAIT_CONFIG_REPLY state + */ + @Test + public void moveToWaitDescriptionStatReply() throws Exception { + moveToWaitConfigReply(); + + connection.clearMessages(); + OFGetConfigReply cr = factory.buildGetConfigReply() + .setMissSendLen(0xFFFF) + .build(); + + switchHandler.processOFMessage(cr); + + OFMessage msg = connection.retrieveMessage(); + assertEquals(OFType.STATS_REQUEST, msg.getType()); + OFStatsRequest<?> sr = (OFStatsRequest<?>)msg; + assertEquals(OFStatsType.DESC, sr.getStatsType()); + verifyUniqueXids(msg); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitDescriptionStatReplyState.class)); + } + + protected OFDescStatsReply createDescriptionStatsReply() { + OFDescStatsReply statsReply = factory.buildDescStatsReply() + .setDpDesc("Datapath Description") + .setHwDesc("Hardware Description") + .setMfrDesc("Manufacturer Description") + .setSwDesc("Software Description") + .setSerialNum("Serial Number") + .build(); + return statsReply; + } + + protected OFTableFeaturesStatsReply createTableFeaturesStatsReply() { + OFTableFeaturesStatsReply statsReply = factory.buildTableFeaturesStatsReply() + .setEntries(Collections.singletonList(factory.buildTableFeatures() + .setConfig(0) + .setMaxEntries(100) + .setMetadataMatch(U64.NO_MASK) + .setMetadataWrite(U64.NO_MASK) + .setName("MyTable") + .setTableId(TableId.of(1)) + .setProperties(Collections.singletonList((OFTableFeatureProp)factory.buildTableFeaturePropMatch() + .setOxmIds(Collections.singletonList(U32.of(100))) + .build()) + ).build() + ) + + ).build(); + return statsReply; + } + + /** + * setup the expectations for the mock switch that are needed + * after the switch is instantiated in the WAIT_DESCRIPTION_STATS STATE + * Will reset the switch + * @throws CounterException + */ + protected void setupSwitchForInstantiationWithReset() + throws Exception { + reset(sw); + sw.setFeaturesReply(featuresReply); + expectLastCall().once(); + } + + /** + * Tests a situation where a switch returns a QUARANTINE result. This means + * we should move the handshake handler to a quarantine state and also + * quarantine the switch in the controller. + * + * @throws Exception + */ + @Test + public void moveQuarantine() throws Exception { + moveToWaitAppHandshakeState(); + + reset(switchManager); + switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.QUARANTINED); + expectLastCall().once(); + replay(switchManager); + + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(WaitAppHandshakeState.class)); + WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting(); + assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin)); + + reset(sw); + expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE); + sw.setStatus(SwitchStatus.QUARANTINED); + expectLastCall().once(); + replay(sw); + + PluginResult result = new PluginResult(PluginResultType.QUARANTINE, "test quarantine"); + handshakePlugin.exitPlugin(result); + + assertThat(switchHandler.getStateForTesting(), + CoreMatchers.instanceOf(QuarantineState.class)); + verify(switchManager); + } + + /** + * Tests a situation where a plugin returns a DISCONNECT result. This means + * we should disconnect the connection and the state should not change. + * + * @throws Exception + */ + @Test + public void failedAppHandshake() throws Exception { + moveToWaitAppHandshakeState(); + + assertThat(switchHandler.getStateForTesting(), + CoreMatchers.instanceOf(WaitAppHandshakeState.class)); + + WaitAppHandshakeState state = (WaitAppHandshakeState) switchHandler.getStateForTesting(); + assertThat(state.getCurrentPlugin(), CoreMatchers.<OFSwitchAppHandshakePlugin>equalTo(handshakePlugin)); + + PluginResult result = new PluginResult(PluginResultType.DISCONNECT); + handshakePlugin.exitPlugin(result); + + assertThat(connection.isConnected(), equalTo(false)); + } + + + @Test + public void validAppHandshakePluginReason() throws Exception { + try{ + new PluginResult(PluginResultType.QUARANTINE,"This should not cause an exception"); + }catch(IllegalStateException e) { + fail("This should cause an illegal state exception"); + } + } + + @Test + public void invalidAppHandshakePluginReason() throws Exception { + try{ + new PluginResult(PluginResultType.CONTINUE,"This should cause an exception"); + fail("This should cause an illegal state exception"); + }catch(IllegalStateException e) { /* Expected */ } + + try{ + new PluginResult(PluginResultType.DISCONNECT,"This should cause an exception"); + fail("This should cause an illegal state exception"); + }catch(IllegalStateException e) { /* Expected */ } + } + + /** + * Move the channel from scratch to WAIT_INITIAL_ROLE state via + * WAIT_SWITCH_DRIVER_SUB_HANDSHAKE + * Does extensive testing for the WAIT_SWITCH_DRIVER_SUB_HANDSHAKE state + * + */ + @Test + public void testSwitchDriverSubHandshake() + throws Exception { + moveToWaitSwitchDriverSubHandshake(); + + //------------------------------------------------- + //------------------------------------------------- + // Send a message to the handler, it should be passed to the + // switch's sub-handshake handling. After this message the + // sub-handshake will be complete + // FIXME:LOJI: With Andi's fix for a default Match object we won't + // need to build/set this match object + + Match match = factory.buildMatch().build(); + OFMessage m = factory.buildFlowRemoved().setMatch(match).build(); + resetToStrict(sw); + sw.processDriverHandshakeMessage(m); + expectLastCall().once(); + expect(sw.isDriverHandshakeComplete()).andReturn(true).once(); + replay(sw); + + switchHandler.processOFMessage(m); + + assertThat(switchHandler.getStateForTesting(), + CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitAppHandshakeState.class)); + assertThat("Unexpected message captured", connection.getMessages(), Matchers.empty()); + verify(sw); + } + + @Test + /** Test WaitDescriptionReplyState */ + public void testWaitDescriptionReplyState() throws Exception { + moveToWaitInitialRole(); + } + + /** + * Setup the mock switch and write capture for a role request, set the + * role and verify mocks. + * @param supportsNxRole whether the switch supports role request messages + * to setup the attribute. This must be null (don't yet know if roles + * supported: send to check) or true. + * @param role The role to send + * @throws IOException + */ + private long setupSwitchSendRoleRequestAndVerify(Boolean supportsNxRole, + OFControllerRole role) throws IOException { + assertTrue("This internal test helper method most not be called " + + "with supportsNxRole==false. Test setup broken", + supportsNxRole == null || supportsNxRole == true); + reset(sw); + expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) + .andReturn(supportsNxRole).atLeastOnce(); + replay(sw); + + switchHandler.sendRoleRequest(role); + + OFMessage msg = connection.retrieveMessage(); + verifyRoleRequest(msg, role); + verify(sw); + return msg.getXid(); + } + + + /** + * Setup the mock switch for a role change request where the switch + * does not support roles. + * + * Needs to verify and reset the controller since we need to set + * an expectation + */ + @SuppressWarnings("unchecked") private void setupSwitchRoleChangeUnsupported(int xid, - OFControllerRole role) { - SwitchStatus newStatus = role != OFControllerRole.ROLE_SLAVE ? SwitchStatus.MASTER : SwitchStatus.SLAVE; - boolean supportsNxRole = false; - verify(switchManager); - reset(sw, switchManager); - expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) - .andReturn(supportsNxRole).atLeastOnce(); - // TODO: hmmm. While it's not incorrect that we set the attribute - // again it looks odd. Maybe change - expect(sw.getOFFactory()).andReturn(factory).anyTimes(); - sw.write(anyObject(OFMessage.class)); - expectLastCall().anyTimes(); - sw.write(anyObject(Iterable.class)); - expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, supportsNxRole); - expectLastCall().anyTimes(); - sw.setControllerRole(role); - expectLastCall().once(); - - if (role == OFControllerRole.ROLE_SLAVE) { - sw.disconnect(); - expectLastCall().once(); - } else { - expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); - sw.setStatus(newStatus); - expectLastCall().once(); - switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, newStatus); - } - replay(sw, switchManager); - - switchHandler.sendRoleRequest(role); - - verify(sw, switchManager); - } - - /** Return a bad request error message with the given xid/code */ - private OFMessage getBadRequestErrorMessage(OFBadRequestCode code, long xid) { - OFErrorMsg msg = factory.errorMsgs().buildBadRequestErrorMsg() - .setXid(xid) - .setCode(code) - .build(); - return msg; - } - - /** Return a bad action error message with the given xid/code */ - private OFMessage getBadActionErrorMessage(OFBadActionCode code, long xid) { - OFErrorMsg msg = factory.errorMsgs().buildBadActionErrorMsg() - .setXid(xid) - .setCode(code) - .build(); - return msg; - } - - - /** Move the channel from scratch to MASTER state - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - * - * This method tests only the simple case that the switch supports roles - * and transitions to MASTER - */ - @SuppressWarnings("unchecked") + OFControllerRole role) { + SwitchStatus newStatus = role != OFControllerRole.ROLE_SLAVE ? SwitchStatus.MASTER : SwitchStatus.SLAVE; + boolean supportsNxRole = false; + verify(switchManager); + reset(sw, switchManager); + expect(sw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) + .andReturn(supportsNxRole).atLeastOnce(); + // TODO: hmmm. While it's not incorrect that we set the attribute + // again it looks odd. Maybe change + expect(sw.getOFFactory()).andReturn(factory).anyTimes(); + sw.write(anyObject(OFMessage.class)); + expectLastCall().anyTimes(); + sw.write(anyObject(Iterable.class)); + expectLastCall().anyTimes(); + expect(sw.getNumTables()).andStubReturn((short)0); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, supportsNxRole); + expectLastCall().anyTimes(); + if (SwitchStatus.MASTER == newStatus) { + if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); + expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); + } + } + + sw.setControllerRole(role); + expectLastCall().once(); + + if (role == OFControllerRole.ROLE_SLAVE) { + sw.disconnect(); + expectLastCall().once(); + } else { + expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); + sw.setStatus(newStatus); + expectLastCall().once(); + switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, newStatus); + } + replay(sw, switchManager); + + switchHandler.sendRoleRequest(role); + + /* Now, trigger transition to master */ + OFBarrierReply br = getFactory().buildBarrierReply() + .build(); + switchHandler.processOFMessage(br); + + verify(sw, switchManager); + } + + /** Return a bad request error message with the given xid/code */ + private OFMessage getBadRequestErrorMessage(OFBadRequestCode code, long xid) { + OFErrorMsg msg = factory.errorMsgs().buildBadRequestErrorMsg() + .setXid(xid) + .setCode(code) + .build(); + return msg; + } + + /** Return a bad action error message with the given xid/code */ + private OFMessage getBadActionErrorMessage(OFBadActionCode code, long xid) { + OFErrorMsg msg = factory.errorMsgs().buildBadActionErrorMsg() + .setXid(xid) + .setCode(code) + .build(); + return msg; + } + + + /** Move the channel from scratch to MASTER state + * Builds on doMoveToWaitInitialRole() + * adds testing for WAIT_INITAL_ROLE state + * + * This method tests only the simple case that the switch supports roles + * and transitions to MASTER + */ + @SuppressWarnings("unchecked") + @Test + public void testInitialMoveToMasterWithRole() throws Exception { + // first, move us to WAIT_INITIAL_ROLE_STATE + moveToWaitInitialRole(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Set the role + long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // prepare mocks and inject the role reply message + reset(sw); + expect(sw.getOFFactory()).andReturn(factory).anyTimes(); + sw.write(anyObject(OFMessage.class)); + expectLastCall().anyTimes(); + sw.write(anyObject(Iterable.class)); + expectLastCall().anyTimes(); + expect(sw.getTables()).andStubReturn(Collections.EMPTY_LIST); + expect(sw.getNumTables()).andStubReturn((short) 0); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_MASTER); + expectLastCall().once(); + expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); + sw.setStatus(SwitchStatus.MASTER); + expectLastCall().once(); + + if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { + //expect(sw.getMaxTableForTableMissFlow()).andReturn(TableId.ZERO).times(1); + expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); + } + replay(sw); + + reset(switchManager); + switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); + expectLastCall().once(); + replay(switchManager); + OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER); + + /* Go into the MasterState */ + switchHandler.processOFMessage(reply); + + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); + } + + /** Move the channel from scratch to SLAVE state + * Builds on doMoveToWaitInitialRole() + * adds testing for WAIT_INITAL_ROLE state + * + * This method tests only the simple case that the switch supports roles + * and transitions to SLAVE + */ + @Test + public void testInitialMoveToSlaveWithRole() throws Exception { + + // first, move us to WAIT_INITIAL_ROLE_STATE + moveToWaitInitialRole(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Set the role + long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // prepare mocks and inject the role reply message + reset(sw); + sw.setAttribute(IOFSwitchBackend.SWITCH_SUPPORTS_NX_ROLE, true); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_SLAVE); + expectLastCall().once(); + expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); + sw.setStatus(SwitchStatus.SLAVE); + expectLastCall().once(); + replay(sw); + + reset(switchManager); + switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.SLAVE); + expectLastCall().once(); + replay(switchManager); + + OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE); + + // sendMessageToHandler will verify and rest controller mock + switchHandler.processOFMessage(reply); + + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class)); + } + + /** Move the channel from scratch to MASTER state + * Builds on doMoveToWaitInitialRole() + * adds testing for WAIT_INITAL_ROLE state + * + * This method tests the case that the switch does NOT support roles. + * The channel handler still needs to send the initial request to find + * out that whether the switch supports roles. + */ + @SuppressWarnings("unchecked") + @Test + public void testInitialMoveToMasterNoRole() throws Exception { + // first, move us to WAIT_INITIAL_ROLE_STATE + moveToWaitInitialRole(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Set the role + long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // prepare mocks and inject the role reply message + reset(sw); + expect(sw.getOFFactory()).andReturn(factory).anyTimes(); + sw.write(anyObject(OFMessage.class)); + expectLastCall().anyTimes(); + sw.write(anyObject(Iterable.class)); + expectLastCall().anyTimes(); + expect(sw.getNumTables()).andStubReturn((short)0); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_MASTER); + expectLastCall().once(); + expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); + sw.setStatus(SwitchStatus.MASTER); + expectLastCall().once(); + if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); + expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); + } + replay(sw); + + // FIXME: shouldn't use ordinal(), but OFError is broken + + // Error with incorrect xid and type. Should be ignored. + OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1); + // sendMessageToHandler will verify and rest controller mock + switchHandler.processOFMessage(err); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Error with correct xid. Should trigger state transition + err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid); + reset(switchManager); + switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); + expectLastCall().once(); + replay(switchManager); + + /* Go into the MasterState */ + switchHandler.processOFMessage(err); + + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); + } + + /** Move the channel from scratch to MASTER state + * Builds on doMoveToWaitInitialRole() + * adds testing for WAIT_INITAL_ROLE state + * + * We let the initial role request time out. Role support should be + * disabled but the switch should be activated. + */ + @SuppressWarnings("unchecked") + @Test + public void testInitialMoveToMasterTimeout() throws Exception { + int timeout = 50; + switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout); + + // first, move us to WAIT_INITIAL_ROLE_STATE + moveToWaitInitialRole(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Set the role + setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); /* don't care about the XID, since we assume the reply is lost */ + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // prepare mocks and inject the role reply message + reset(sw); + expect(sw.getOFFactory()).andReturn(factory).anyTimes(); + sw.write(anyObject(OFMessage.class)); + expectLastCall().anyTimes(); + sw.write(anyObject(Iterable.class)); + expectLastCall().anyTimes(); + expect(sw.getNumTables()).andStubReturn((short)0); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_MASTER); + expectLastCall().once(); + expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); + sw.setStatus(SwitchStatus.MASTER); + expectLastCall().once(); + if (factory.getVersion().compareTo(OFVersion.OF_13) >= 0) { + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); + expect(sw.getTableFeatures(TableId.ZERO)).andReturn(TableFeatures.of(createTableFeaturesStatsReply().getEntries().get(0))).anyTimes(); + } + replay(sw); + + OFMessage m = factory.barrierReply(); + + Thread.sleep(timeout + 5); + + reset(switchManager); + switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); + expectLastCall().once(); + replay(switchManager); + switchHandler.processOFMessage(m); + + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); + + } + + + /** Move the channel from scratch to SLAVE state + * Builds on doMoveToWaitInitialRole() + * adds testing for WAIT_INITAL_ROLE state + * + * This method tests the case that the switch does NOT support roles. + * The channel handler still needs to send the initial request to find + * out that whether the switch supports roles. + * + */ @Test - public void testInitialMoveToMasterWithRole() throws Exception { - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Set the role - long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // prepare mocks and inject the role reply message - reset(sw); - expect(sw.getOFFactory()).andReturn(factory).anyTimes(); - sw.write(anyObject(OFMessage.class)); - expectLastCall().anyTimes(); - sw.write(anyObject(Iterable.class)); - expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_MASTER); - expectLastCall().once(); - expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); - sw.setStatus(SwitchStatus.MASTER); - expectLastCall().once(); - replay(sw); - - reset(switchManager); - switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); - expectLastCall().once(); - replay(switchManager); - OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER); - - // sendMessageToHandler will verify and rest controller mock - switchHandler.processOFMessage(reply); - - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); - } - - /** Move the channel from scratch to SLAVE state - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - * - * This method tests only the simple case that the switch supports roles - * and transitions to SLAVE - */ - @Test - public void testInitialMoveToSlaveWithRole() throws Exception { - - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Set the role - long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // prepare mocks and inject the role reply message - reset(sw); - sw.setAttribute(IOFSwitchBackend.SWITCH_SUPPORTS_NX_ROLE, true); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_SLAVE); - expectLastCall().once(); - expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); - sw.setStatus(SwitchStatus.SLAVE); - expectLastCall().once(); - replay(sw); - - reset(switchManager); - switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.SLAVE); - expectLastCall().once(); - replay(switchManager); - - OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE); - - // sendMessageToHandler will verify and rest controller mock - switchHandler.processOFMessage(reply); - - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class)); - } - - /** Move the channel from scratch to MASTER state - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - * - * This method tests the case that the switch does NOT support roles. - * The channel handler still needs to send the initial request to find - * out that whether the switch supports roles. - */ - @SuppressWarnings("unchecked") + public void testInitialMoveToSlaveNoRole() throws Exception { + // first, move us to WAIT_INITIAL_ROLE_STATE + moveToWaitInitialRole(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Set the role + long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // prepare mocks and inject the role reply message + reset(sw); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_SLAVE); + expectLastCall().once(); + sw.disconnect(); // Make sure we disconnect + expectLastCall().once(); + replay(sw); + + + // FIXME: shouldn't use ordinal(), but OFError is broken + + // Error with incorrect xid and type. Should be ignored. + OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1); + + // sendMessageToHandler will verify and rest controller mock + switchHandler.processOFMessage(err); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Error with correct xid. Should trigger state transition + err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid); + // sendMessageToHandler will verify and rest controller mock + switchHandler.processOFMessage(err); + } + + + /** Move the channel from scratch to SLAVE state + * Builds on doMoveToWaitInitialRole() + * adds testing for WAIT_INITAL_ROLE state + * + * We let the initial role request time out. The switch should be + * disconnected + */ @Test - public void testInitialMoveToMasterNoRole() throws Exception { - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Set the role - long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // prepare mocks and inject the role reply message - reset(sw); - expect(sw.getOFFactory()).andReturn(factory).anyTimes(); - sw.write(anyObject(OFMessage.class)); - expectLastCall().anyTimes(); - sw.write(anyObject(Iterable.class)); - expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_MASTER); - expectLastCall().once(); - expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); - sw.setStatus(SwitchStatus.MASTER); - expectLastCall().once(); - replay(sw); - - // FIXME: shouldn't use ordinal(), but OFError is broken - - // Error with incorrect xid and type. Should be ignored. - OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1); - // sendMessageToHandler will verify and rest controller mock - switchHandler.processOFMessage(err); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Error with correct xid. Should trigger state transition - err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid); - reset(switchManager); - switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); - expectLastCall().once(); - replay(switchManager); - // sendMessageToHandler will verify and rest controller mock - switchHandler.processOFMessage(err); - - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); - } - - /** Move the channel from scratch to MASTER state - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - * - * We let the initial role request time out. Role support should be - * disabled but the switch should be activated. - */ - @SuppressWarnings("unchecked") + public void testInitialMoveToSlaveTimeout() throws Exception { + int timeout = 50; + switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout); + + // first, move us to WAIT_INITIAL_ROLE_STATE + moveToWaitInitialRole(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Set the role + setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // prepare mocks and inject the role reply message + reset(sw); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_SLAVE); + expectLastCall().once(); + sw.disconnect(); // Make sure we disconnect + expectLastCall().once(); + replay(sw); + + // Apparently this can be any type of message for this test?! + OFMessage m = factory.buildBarrierReply().build(); + + Thread.sleep(timeout+5); + switchHandler.processOFMessage(m); + } + + + /** Move channel from scratch to WAIT_INITIAL_STATE, then MASTER, + * then SLAVE for cases where the switch does not support roles. + * I.e., the final SLAVE transition should disconnect the switch. + */ @Test - public void testInitialMoveToMasterTimeout() throws Exception { - int timeout = 50; - switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout); - - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Set the role - setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // prepare mocks and inject the role reply message - reset(sw); - expect(sw.getOFFactory()).andReturn(factory).anyTimes(); - sw.write(anyObject(OFMessage.class)); - expectLastCall().anyTimes(); - sw.write(anyObject(Iterable.class)); - expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_MASTER); - expectLastCall().once(); - expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); - sw.setStatus(SwitchStatus.MASTER); - expectLastCall().once(); - replay(sw); - - OFMessage m = factory.buildBarrierReply().build(); - - Thread.sleep(timeout+5); - - reset(switchManager); - switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); - expectLastCall().once(); - replay(switchManager); - switchHandler.processOFMessage(m); - - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); - - } - - - /** Move the channel from scratch to SLAVE state - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - * - * This method tests the case that the switch does NOT support roles. - * The channel handler still needs to send the initial request to find - * out that whether the switch supports roles. - * - */ - @Test - public void testInitialMoveToSlaveNoRole() throws Exception { - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Set the role - long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // prepare mocks and inject the role reply message - reset(sw); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_SLAVE); - expectLastCall().once(); - sw.disconnect(); // Make sure we disconnect - expectLastCall().once(); - replay(sw); - - - // FIXME: shouldn't use ordinal(), but OFError is broken - - // Error with incorrect xid and type. Should be ignored. - OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid+1); - - // sendMessageToHandler will verify and rest controller mock - switchHandler.processOFMessage(err); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Error with correct xid. Should trigger state transition - err = getBadRequestErrorMessage(OFBadRequestCode.BAD_EXPERIMENTER, xid); - // sendMessageToHandler will verify and rest controller mock - switchHandler.processOFMessage(err); - } - - - /** Move the channel from scratch to SLAVE state - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - * - * We let the initial role request time out. The switch should be - * disconnected - */ - @Test - public void testInitialMoveToSlaveTimeout() throws Exception { - int timeout = 50; - switchHandler.useRoleChangerWithOtherTimeoutForTesting(timeout); - - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Set the role - setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_SLAVE); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // prepare mocks and inject the role reply message - reset(sw); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, false); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_SLAVE); - expectLastCall().once(); - sw.disconnect(); // Make sure we disconnect - expectLastCall().once(); - replay(sw); - - // Apparently this can be any type of message for this test?! - OFMessage m = factory.buildBarrierReply().build(); - - Thread.sleep(timeout+5); - switchHandler.processOFMessage(m); - } - - - /** Move channel from scratch to WAIT_INITIAL_STATE, then MASTER, - * then SLAVE for cases where the switch does not support roles. - * I.e., the final SLAVE transition should disconnect the switch. - */ - @Test - public void testNoRoleInitialToMasterToSlave() throws Exception { - int xid = 46; - // First, lets move the state to MASTER without role support - testInitialMoveToMasterNoRole(); - assertThat(switchHandler.getStateForTesting(), - CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); - - assertThat("Unexpected messages have been captured", - connection.getMessages(), - Matchers.empty()); - - // try to set master role again. should be a no-op - setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_MASTER); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); - - assertThat("Unexpected messages have been captured", - connection.getMessages(), - Matchers.empty()); - - setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_SLAVE); - assertThat(connection.isConnected(), equalTo(false)); - - assertThat("Unexpected messages have been captured", - connection.getMessages(), - Matchers.empty()); - } - - /** Move the channel to MASTER state - * Expects that the channel is in MASTER or SLAVE state. - * - */ - @SuppressWarnings("unchecked") + public void testNoRoleInitialToMasterToSlave() throws Exception { + int xid = 46; + // First, lets move the state to MASTER without role support + testInitialMoveToMasterNoRole(); + assertThat(switchHandler.getStateForTesting(), + CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); + + assertThat("Unexpected messages have been captured", + connection.getMessages(), + Matchers.empty()); + + // try to set master role again. should be a no-op + setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_MASTER); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); + + assertThat("Unexpected messages have been captured", + connection.getMessages(), + Matchers.empty()); + + setupSwitchRoleChangeUnsupported(xid, OFControllerRole.ROLE_SLAVE); + assertThat(connection.isConnected(), equalTo(false)); + + assertThat("Unexpected messages have been captured", + connection.getMessages(), + Matchers.empty()); + } + + /** Move the channel to MASTER state + * Expects that the channel is in MASTER or SLAVE state. + * + */ + @SuppressWarnings("unchecked") public void changeRoleToMasterWithRequest() throws Exception { - assertTrue("This method can only be called when handler is in " + - "MASTER or SLAVE role", switchHandler.isHandshakeComplete()); - - // Set the role - long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_MASTER); - - // prepare mocks and inject the role reply message - reset(sw); - expect(sw.getOFFactory()).andReturn(factory).anyTimes(); - sw.write(anyObject(OFMessage.class)); - expectLastCall().anyTimes(); - sw.write(anyObject(Iterable.class)); - expectLastCall().anyTimes(); - expect(sw.getTables()).andStubReturn((short)0); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_MASTER); - expectLastCall().once(); - expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); - sw.setStatus(SwitchStatus.MASTER); - expectLastCall().once(); - replay(sw); - - reset(switchManager); - switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); - expectLastCall().once(); - replay(switchManager); - - OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER); - - switchHandler.processOFMessage(reply); - - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); - } - - /** Move the channel to SLAVE state - * Expects that the channel is in MASTER or SLAVE state. - * - */ - public void changeRoleToSlaveWithRequest() throws Exception { - assertTrue("This method can only be called when handler is in " + - "MASTER or SLAVE role", switchHandler.isHandshakeComplete()); - - // Set the role - long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_SLAVE); - - // prepare mocks and inject the role reply message - reset(sw); - sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); - expectLastCall().once(); - sw.setControllerRole(OFControllerRole.ROLE_SLAVE); - expectLastCall().once(); - expect(sw.getStatus()).andReturn(SwitchStatus.MASTER).once(); - sw.setStatus(SwitchStatus.SLAVE); - expectLastCall().once(); - replay(sw); - - reset(switchManager); - switchManager.switchStatusChanged(sw, SwitchStatus.MASTER, SwitchStatus.SLAVE); - expectLastCall().once(); - replay(switchManager); - - OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE); - connection.getListener().messageReceived(connection, reply); - - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class)); - } - - @Test - public void testMultiRoleChange1() throws Exception { - testInitialMoveToMasterWithRole(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - } - - @Test - public void testMultiRoleChange2() throws Exception { - testInitialMoveToSlaveWithRole(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToSlaveWithRequest(); - changeRoleToMasterWithRequest(); - changeRoleToSlaveWithRequest(); - } - - /** Start from scratch and reply with an unexpected error to the role - * change request - * Builds on doMoveToWaitInitialRole() - * adds testing for WAIT_INITAL_ROLE state - */ - @Test - public void testInitialRoleChangeOtherError() throws Exception { - // first, move us to WAIT_INITIAL_ROLE_STATE - moveToWaitInitialRole(); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - // Set the role - long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); - assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); - - OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid); - - verifyExceptionCaptured(err, SwitchStateException.class); - } - - /** - * Test dispatch of messages while in MASTER role - */ - @Test - public void testMessageDispatchMaster() throws Exception { - testInitialMoveToMasterWithRole(); - - // Send packet in. expect dispatch - OFPacketIn pi = factory.buildPacketIn() - .setReason(OFPacketInReason.NO_MATCH) - .build(); - reset(switchManager); - switchManager.handleMessage(sw, pi, null); - expectLastCall().once(); - replay(switchManager); - switchHandler.processOFMessage(pi); - - // TODO: many more to go - } - - /** - * Test port status message handling while MASTER - * - */ - @Test - public void testPortStatusMessageMaster() throws Exception { - DatapathId dpid = featuresReply.getDatapathId(); - testInitialMoveToMasterWithRole(); - - OFPortDesc portDesc = factory.buildPortDesc() - .setName("Port1") - .setPortNo(OFPort.of(1)) - .build(); - OFPortStatus.Builder portStatusBuilder = factory.buildPortStatus() - .setDesc(portDesc); - - // The events we expect sw.handlePortStatus to return - // We'll just use the same list for all valid OFPortReasons and add - // arbitrary events for arbitrary ports that are not necessarily - // related to the port status message. Our goal - // here is not to return the correct set of events but the make sure - // that a) sw.handlePortStatus is called - // b) the list of events sw.handlePortStatus returns is sent - // as IOFSwitchListener notifications. - OrderedCollection<PortChangeEvent> events = - new LinkedHashSetWrapper<PortChangeEvent>(); - OFPortDesc.Builder pb = factory.buildPortDesc(); - OFPortDesc p1 = pb.setName("eth1").setPortNo(OFPort.of(1)).build(); - OFPortDesc p2 = pb.setName("eth2").setPortNo(OFPort.of(2)).build(); - OFPortDesc p3 = pb.setName("eth3").setPortNo(OFPort.of(3)).build(); - OFPortDesc p4 = pb.setName("eth4").setPortNo(OFPort.of(4)).build(); - OFPortDesc p5 = pb.setName("eth5").setPortNo(OFPort.of(5)).build(); - - - events.add(new PortChangeEvent(p1, PortChangeType.ADD)); - events.add(new PortChangeEvent(p2, PortChangeType.DELETE)); - events.add(new PortChangeEvent(p3, PortChangeType.UP)); - events.add(new PortChangeEvent(p4, PortChangeType.DOWN)); - events.add(new PortChangeEvent(p5, PortChangeType.OTHER_UPDATE)); - - - for (OFPortReason reason: OFPortReason.values()) { - OFPortStatus portStatus = portStatusBuilder.setReason(reason).build(); - - reset(sw); - expect(sw.getId()).andReturn(dpid).anyTimes(); - - expect(sw.processOFPortStatus(portStatus)).andReturn(events).once(); - replay(sw); - - reset(switchManager); - switchManager.notifyPortChanged(sw, p1, PortChangeType.ADD); - switchManager.notifyPortChanged(sw, p2, PortChangeType.DELETE); - switchManager.notifyPortChanged(sw, p3, PortChangeType.UP); - switchManager.notifyPortChanged(sw, p4, PortChangeType.DOWN); - switchManager.notifyPortChanged(sw, p5, PortChangeType.OTHER_UPDATE); - replay(switchManager); - - switchHandler.processOFMessage(portStatus); - - verify(sw); - } - } - - /** - * Test re-assert MASTER - * - */ - @Test - public void testReassertMaster() throws Exception { - testInitialMoveToMasterWithRole(); - - OFMessage err = getBadRequestErrorMessage(OFBadRequestCode.EPERM, 42); - - reset(roleManager); - roleManager.reassertRole(switchHandler, HARole.ACTIVE); - expectLastCall().once(); - replay(roleManager); - - reset(switchManager); - switchManager.handleMessage(sw, err, null); - expectLastCall().once(); - replay(switchManager); - - switchHandler.processOFMessage(err); - - verify(sw); - } - - /** - * Verify that the given exception event capture (as returned by - * getAndInitExceptionCapture) has thrown an exception of the given - * expectedExceptionClass. - * Resets the capture - * @param err - */ - void verifyExceptionCaptured( - OFMessage err, Class<? extends Throwable> expectedExceptionClass) { - - Throwable caughtEx = null; - // This should purposely cause an exception - try{ - switchHandler.processOFMessage(err); - } - catch(Exception e){ - // Capture the exception - caughtEx = e; - } - - assertThat(caughtEx, CoreMatchers.instanceOf(expectedExceptionClass)); - } - - /** - * Tests the connection closed functionality before the switch handshake is complete. - * Essentially when the switch handshake is only aware of the IOFConnection. - */ - @Test - public void testConnectionClosedBeforeHandshakeComplete() { - - // Test connection closed prior to being finished - reset(switchManager); - switchManager.handshakeDisconnected(dpid); - expectLastCall().once(); - replay(switchManager); - - switchHandler.connectionClosed(connection); - - verify(switchManager); - } - - /** - * Tests the connection closed functionality after the switch handshake is complete. - * Essentially when the switch handshake is aware of an IOFSwitch. - * @throws Exception - */ - @Test - public void testConnectionClosedAfterHandshakeComplete() throws Exception { - - testInitialMoveToMasterWithRole(); - // Test connection closed prior to being finished - reset(switchManager); - switchManager.handshakeDisconnected(dpid); - expectLastCall().once(); - switchManager.switchDisconnected(sw); - expectLastCall().once(); - replay(switchManager); - - reset(sw); - expect(sw.getStatus()).andReturn(SwitchStatus.DISCONNECTED).anyTimes(); - replay(sw); - - switchHandler.connectionClosed(connection); - - verify(switchManager); - verify(sw); - } + assertTrue("This method can only be called when handler is in " + + "MASTER or SLAVE role", switchHandler.isHandshakeComplete()); + + // Set the role + long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_MASTER); + + // prepare mocks and inject the role reply message + reset(sw); + expect(sw.getOFFactory()).andReturn(factory).anyTimes(); + sw.write(anyObject(OFMessage.class)); + expectLastCall().anyTimes(); + sw.write(anyObject(Iterable.class)); + expectLastCall().anyTimes(); + expect(sw.getNumTables()).andStubReturn((short)0); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_MASTER); + expectLastCall().once(); + expect(sw.getStatus()).andReturn(SwitchStatus.HANDSHAKE).once(); + sw.setStatus(SwitchStatus.MASTER); + expectLastCall().once(); + expect(sw.getTables()).andReturn(Collections.EMPTY_LIST).once(); + replay(sw); + + reset(switchManager); + switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); + expectLastCall().once(); + replay(switchManager); + + OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_MASTER); + + switchHandler.processOFMessage(reply); + + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.MasterState.class)); + } + + /** Move the channel to SLAVE state + * Expects that the channel is in MASTER or SLAVE state. + * + */ + public void changeRoleToSlaveWithRequest() throws Exception { + assertTrue("This method can only be called when handler is in " + + "MASTER or SLAVE role", switchHandler.isHandshakeComplete()); + + // Set the role + long xid = setupSwitchSendRoleRequestAndVerify(true, OFControllerRole.ROLE_SLAVE); + + // prepare mocks and inject the role reply message + reset(sw); + sw.setAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE, true); + expectLastCall().once(); + sw.setControllerRole(OFControllerRole.ROLE_SLAVE); + expectLastCall().once(); + expect(sw.getStatus()).andReturn(SwitchStatus.MASTER).once(); + sw.setStatus(SwitchStatus.SLAVE); + expectLastCall().once(); + replay(sw); + + reset(switchManager); + switchManager.switchStatusChanged(sw, SwitchStatus.MASTER, SwitchStatus.SLAVE); + expectLastCall().once(); + replay(switchManager); + + OFMessage reply = getRoleReply(xid, OFControllerRole.ROLE_SLAVE); + connection.getListener().messageReceived(connection, reply); + + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.SlaveState.class)); + } + + @Test + public void testMultiRoleChange1() throws Exception { + testInitialMoveToMasterWithRole(); + changeRoleToMasterWithRequest(); + changeRoleToSlaveWithRequest(); + changeRoleToSlaveWithRequest(); + changeRoleToMasterWithRequest(); + changeRoleToSlaveWithRequest(); + } + + @Test + public void testMultiRoleChange2() throws Exception { + testInitialMoveToSlaveWithRole(); + changeRoleToMasterWithRequest(); + changeRoleToSlaveWithRequest(); + changeRoleToSlaveWithRequest(); + changeRoleToMasterWithRequest(); + changeRoleToSlaveWithRequest(); + } + + /** Start from scratch and reply with an unexpected error to the role + * change request + * Builds on doMoveToWaitInitialRole() + * adds testing for WAIT_INITAL_ROLE state + */ + @Test + public void testInitialRoleChangeOtherError() throws Exception { + // first, move us to WAIT_INITIAL_ROLE_STATE + moveToWaitInitialRole(); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + // Set the role + long xid = setupSwitchSendRoleRequestAndVerify(null, OFControllerRole.ROLE_MASTER); + assertThat(switchHandler.getStateForTesting(), CoreMatchers.instanceOf(OFSwitchHandshakeHandler.WaitInitialRoleState.class)); + + OFMessage err = getBadActionErrorMessage(OFBadActionCode.BAD_TYPE, xid); + + verifyExceptionCaptured(err, SwitchStateException.class); + } + + /** + * Test dispatch of messages while in MASTER role + */ + @Test + public void testMessageDispatchMaster() throws Exception { + testInitialMoveToMasterWithRole(); + + // Send packet in. expect dispatch + OFPacketIn pi = factory.buildPacketIn() + .setReason(OFPacketInReason.NO_MATCH) + .build(); + reset(switchManager); + switchManager.handleMessage(sw, pi, null); + expectLastCall().once(); + replay(switchManager); + switchHandler.processOFMessage(pi); + + // TODO: many more to go + } + + /** + * Test port status message handling while MASTER + * + */ + @Test + public void testPortStatusMessageMaster() throws Exception { + DatapathId dpid = featuresReply.getDatapathId(); + testInitialMoveToMasterWithRole(); + + OFPortDesc portDesc = factory.buildPortDesc() + .setName("Port1") + .setPortNo(OFPort.of(1)) + .build(); + OFPortStatus.Builder portStatusBuilder = factory.buildPortStatus() + .setDesc(portDesc); + + // The events we expect sw.handlePortStatus to return + // We'll just use the same list for all valid OFPortReasons and add + // arbitrary events for arbitrary ports that are not necessarily + // related to the port status message. Our goal + // here is not to return the correct set of events but the make sure + // that a) sw.handlePortStatus is called + // b) the list of events sw.handlePortStatus returns is sent + // as IOFSwitchListener notifications. + OrderedCollection<PortChangeEvent> events = + new LinkedHashSetWrapper<PortChangeEvent>(); + OFPortDesc.Builder pb = factory.buildPortDesc(); + OFPortDesc p1 = pb.setName("eth1").setPortNo(OFPort.of(1)).build(); + OFPortDesc p2 = pb.setName("eth2").setPortNo(OFPort.of(2)).build(); + OFPortDesc p3 = pb.setName("eth3").setPortNo(OFPort.of(3)).build(); + OFPortDesc p4 = pb.setName("eth4").setPortNo(OFPort.of(4)).build(); + OFPortDesc p5 = pb.setName("eth5").setPortNo(OFPort.of(5)).build(); + + + events.add(new PortChangeEvent(p1, PortChangeType.ADD)); + events.add(new PortChangeEvent(p2, PortChangeType.DELETE)); + events.add(new PortChangeEvent(p3, PortChangeType.UP)); + events.add(new PortChangeEvent(p4, PortChangeType.DOWN)); + events.add(new PortChangeEvent(p5, PortChangeType.OTHER_UPDATE)); + + + for (OFPortReason reason: OFPortReason.values()) { + OFPortStatus portStatus = portStatusBuilder.setReason(reason).build(); + + reset(sw); + expect(sw.getId()).andReturn(dpid).anyTimes(); + + expect(sw.processOFPortStatus(portStatus)).andReturn(events).once(); + replay(sw); + + reset(switchManager); + switchManager.notifyPortChanged(sw, p1, PortChangeType.ADD); + switchManager.notifyPortChanged(sw, p2, PortChangeType.DELETE); + switchManager.notifyPortChanged(sw, p3, PortChangeType.UP); + switchManager.notifyPortChanged(sw, p4, PortChangeType.DOWN); + switchManager.notifyPortChanged(sw, p5, PortChangeType.OTHER_UPDATE); + replay(switchManager); + + switchHandler.processOFMessage(portStatus); + + verify(sw); + } + } + + /** + * Test re-assert MASTER + * + */ + @Test + public void testReassertMaster() throws Exception { + testInitialMoveToMasterWithRole(); + + OFMessage err = getBadRequestErrorMessage(OFBadRequestCode.EPERM, 42); + + reset(roleManager); + roleManager.reassertRole(switchHandler, HARole.ACTIVE); + expectLastCall().once(); + replay(roleManager); + + reset(switchManager); + switchManager.handleMessage(sw, err, null); + expectLastCall().once(); + replay(switchManager); + + switchHandler.processOFMessage(err); + + verify(sw); + } + + /** + * Verify that the given exception event capture (as returned by + * getAndInitExceptionCapture) has thrown an exception of the given + * expectedExceptionClass. + * Resets the capture + * @param err + */ + void verifyExceptionCaptured( + OFMessage err, Class<? extends Throwable> expectedExceptionClass) { + + Throwable caughtEx = null; + // This should purposely cause an exception + try{ + switchHandler.processOFMessage(err); + } + catch(Exception e){ + // Capture the exception + caughtEx = e; + } + + assertThat(caughtEx, CoreMatchers.instanceOf(expectedExceptionClass)); + } + + /** + * Tests the connection closed functionality before the switch handshake is complete. + * Essentially when the switch handshake is only aware of the IOFConnection. + */ + @Test + public void testConnectionClosedBeforeHandshakeComplete() { + + // Test connection closed prior to being finished + reset(switchManager); + switchManager.handshakeDisconnected(dpid); + expectLastCall().once(); + replay(switchManager); + + switchHandler.connectionClosed(connection); + + verify(switchManager); + } + + /** + * Tests the connection closed functionality after the switch handshake is complete. + * Essentially when the switch handshake is aware of an IOFSwitch. + * @throws Exception + */ + @Test + public void testConnectionClosedAfterHandshakeComplete() throws Exception { + + testInitialMoveToMasterWithRole(); + // Test connection closed prior to being finished + reset(switchManager); + switchManager.handshakeDisconnected(dpid); + expectLastCall().once(); + switchManager.switchDisconnected(sw); + expectLastCall().once(); + replay(switchManager); + + reset(sw); + expect(sw.getStatus()).andReturn(SwitchStatus.DISCONNECTED).anyTimes(); + replay(sw); + + switchHandler.connectionClosed(connection); + + verify(switchManager); + verify(sw); + } } diff --git a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java index 0a4ff826de222f4307ba98e4e83d2b305768206a..62d43ab96a1305cb89e0df40ae14df5ee9f89fd2 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/OFSwitchManagerTest.java @@ -198,7 +198,7 @@ public class OFSwitchManagerTest{ expect(sw.getSwitchDescription()).andReturn(description).anyTimes(); expect(sw.getBuffers()) .andReturn(featuresReply.getNBuffers()).anyTimes(); - expect(sw.getTables()) + expect(sw.getNumTables()) .andReturn(featuresReply.getNTables()).anyTimes(); expect(sw.getCapabilities()) .andReturn(featuresReply.getCapabilities()).anyTimes(); @@ -258,6 +258,7 @@ public class OFSwitchManagerTest{ expect(sw.getStatus()).andReturn(SwitchStatus.MASTER).anyTimes(); sw.disconnect(); expectLastCall().once(); + expect(sw.getOFFactory()).andReturn(factory).once(); replay(sw, listener); // nothing recorded switchManager.switchAdded(sw); switchManager.switchStatusChanged(sw, SwitchStatus.HANDSHAKE, SwitchStatus.MASTER); diff --git a/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java b/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java index 2bd6d0c376585831fb8d37f273a0aa0ed2ae71c3..6e8f51ccfb243880ffd689ec3428e197fbbcc1be 100644 --- a/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java +++ b/src/test/java/net/floodlightcontroller/core/test/MockSwitchManager.java @@ -90,6 +90,12 @@ public class MockSwitchManager implements IFloodlightModule, IOFSwitchManager, I // do nothing } + + @Override + public void handleOutgoingMessage(IOFSwitch sw, OFMessage m) { + // do nothing + + } public void setSwitchHandshakeHandlers(Map<DatapathId, OFSwitchHandshakeHandler> handlers) { this.switchHandlers = handlers; diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 525fdcbe8f920e9f886ea113db31ceb1237caa3c..0bb86f00d0031948b33f5ef21382f50d09694aa3 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -79,6 +79,7 @@ import net.floodlightcontroller.packet.Data; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.IPv6; import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.restserver.RestApiServer; @@ -99,6 +100,7 @@ import org.projectfloodlight.openflow.protocol.match.MatchField; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFPort; @@ -118,8 +120,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { protected OFPacketIn testARPReplyPacketIn_1, testARPReplyPacketIn_2; protected OFPacketIn testUDPPacketIn; + protected OFPacketIn testUDPIPv6PacketIn; + protected OFPacketIn testUDPIPv6RevPacketIn; protected IPacket testARPReplyPacket_1, testARPReplyPacket_2; protected Ethernet testUDPPacket; + protected Ethernet testUDPIPv6Packet; + protected Ethernet testUDPIPv6RevPacket; protected byte[] testARPReplyPacket_1_Srld, testARPReplyPacket_2_Srld; protected byte[] testUDPPacketSrld; private MockSyncService syncService; @@ -243,10 +249,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) - .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01")) - .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); + .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01")) + .setSenderProtocolAddress(IPv4Address.of("192.168.1.1")) + .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55")) + .setTargetProtocolAddress(IPv4Address.of("192.168.1.2"))); this.testARPReplyPacket_1_Srld = testARPReplyPacket_1.serialize(); // Another test packet with the same ARP payload as packet 1 but with @@ -263,10 +269,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) - .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01")) - .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); + .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01")) + .setSenderProtocolAddress(IPv4Address.of("192.168.1.1")) + .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55")) + .setTargetProtocolAddress(IPv4Address.of("192.168.1.2"))); this.testARPReplyPacket_2_Srld = testARPReplyPacket_2.serialize(); // This packet reverses the MACs and IP from testARPReplyPacket_1 @@ -286,6 +292,38 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .setPayload(new Data(new byte[] {0x01})))); updateUDPPacketIn(); + // This packet is used to test forming an entity from an IPv6 packet + this.testUDPIPv6Packet = (Ethernet) new Ethernet() + .setSourceMACAddress("00:11:22:33:44:55") + .setDestinationMACAddress("00:44:33:22:11:01") + .setEtherType(EthType.IPv6) + .setVlanID((short)5) + .setPayload( + new IPv6() + .setHopLimit((byte) 128) + .setSourceAddress(IPv6Address.of(1,1)) + .setDestinationAddress(IPv6Address.of(2,2)) + .setPayload(new UDP() + .setSourcePort((short) 5000) + .setDestinationPort((short) 5001) + .setPayload(new Data(new byte[] {0x01})))); + + // This packet reverses the above + this.testUDPIPv6RevPacket = (Ethernet) new Ethernet() + .setSourceMACAddress("00:44:33:22:11:01") + .setDestinationMACAddress("00:11:22:33:44:55") + .setEtherType(EthType.IPv6) + .setVlanID((short)5) + .setPayload( + new IPv6() + .setHopLimit((byte) 128) + .setSourceAddress(IPv6Address.of(2,2)) + .setDestinationAddress(IPv6Address.of(1,1)) + .setPayload(new UDP() + .setSourcePort((short) 5001) + .setDestinationPort((short) 5000) + .setPayload(new Data(new byte[] {0x01})))); + // Build the PacketIn this.testARPReplyPacketIn_1 = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn() .setBufferId(OFBufferId.NO_BUFFER) @@ -301,6 +339,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .setData(this.testARPReplyPacket_2_Srld) .setReason(OFPacketInReason.NO_MATCH) .build(); + + this.testUDPIPv6PacketIn = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn() + .setBufferId(OFBufferId.NO_BUFFER) + .setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build()) + .setData(this.testUDPIPv6Packet.serialize()) + .setReason(OFPacketInReason.NO_MATCH) + .build(); + + this.testUDPIPv6RevPacketIn = OFFactories.getFactory(OFVersion.OF_13).buildPacketIn() + .setBufferId(OFBufferId.NO_BUFFER) + .setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(1)).build()) + .setData(this.testUDPIPv6RevPacket.serialize()) + .setReason(OFPacketInReason.NO_MATCH) + .build(); } /** @@ -322,9 +374,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase { public void testLastSeen() throws Exception { Calendar c = Calendar.getInstance(); Date d1 = c.getTime(); - Entity entity1 = new Entity(MacAddress.of(1L), null, null, null, null, d1); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO /* untagged*/, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, d1); c.add(Calendar.SECOND, 1); - Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), null, null, c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO /* untagged*/, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, c.getTime()); IDevice d = deviceManager.learnDeviceByEntity(entity2); assertEquals(c.getTime(), d.getLastSeen()); @@ -377,13 +429,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.topology = mockTopology; - Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), new Date()); - Entity entity3 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(10L), OFPort.of(1), new Date()); - Entity entity4 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity5 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), DatapathId.of(5L), OFPort.of(2), new Date()); - Entity entity6 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), DatapathId.of(50L), OFPort.of(3), new Date()); - Entity entity7 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(2), DatapathId.of(50L), OFPort.of(3), new Date()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date()); + Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity5 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date()); + Entity entity6 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(50L), OFPort.of(3), new Date()); + Entity entity7 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(50L), OFPort.of(3), new Date()); mockListener.deviceAdded(isA(IDevice.class)); replay(mockListener, mockTopology); @@ -393,7 +445,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertSame(d1, deviceManager.findDeviceByEntity(entity1)); assertEquals(DefaultEntityClassifier.entityClass , d1.getEntityClass()); - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d1.getVlanId()); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d1.getVlanId()); assertArrayEquals(new IPv4Address[] { }, d1.getIPv4Addresses()); assertEquals(1, deviceManager.getAllDevices().size()); @@ -408,8 +460,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertNotSame(d1, d2); assertNotSame(d1.getDeviceKey(), d2.getDeviceKey()); assertEquals(MockEntityClassifier.testEC, d2.getEntityClass()); - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d2.getVlanId()); - assertArrayEquals(new Integer[] { }, d2.getIPv4Addresses()); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d2.getVlanId()); + assertArrayEquals(new IPv4Address[] { }, d2.getIPv4Addresses()); assertEquals(2, deviceManager.getAllDevices().size()); verify(mockListener); @@ -428,7 +480,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d3.getAttachmentPoints()); assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) }, d3.getAttachmentPoints(true)); - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d3.getVlanId()); assertEquals(2, deviceManager.getAllDevices().size()); @@ -446,7 +498,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d4.getIPv4Addresses()); assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) }, d4.getAttachmentPoints()); - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d4.getVlanId()); assertEquals(2, deviceManager.getAllDevices().size()); @@ -504,15 +556,184 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.entityClassifier = new MockEntityClassifierMac(); deviceManager.startUp(null); - Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), DatapathId.of(-1L), OFPort.of(1), new Date()); + Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), IPv6Address.NONE, DatapathId.of(-1L), OFPort.of(1), new Date()); assertEquals(null, deviceManager.learnDeviceByEntity(entityNoClass)); verify(mockListener); } + @Test + public void testEntityLearningIPv6() throws Exception { + IDeviceListener mockListener = + createMock(IDeviceListener.class); + expect(mockListener.getName()).andReturn("mockListener").atLeastOnce(); + expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject())) + .andReturn(false).atLeastOnce(); + expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject())) + .andReturn(false).atLeastOnce(); + + replay(mockListener); + deviceManager.addListener(mockListener); + verify(mockListener); + reset(mockListener); + deviceManager.entityClassifier= new MockEntityClassifier(); + deviceManager.startUp(null); + + ITopologyService mockTopology = createMock(ITopologyService.class); + expect(mockTopology.getL2DomainId(DatapathId.of(anyLong()))). + andReturn(DatapathId.of(1L)).anyTimes(); + expect(mockTopology.isBroadcastDomainPort(DatapathId.of(anyLong()), OFPort.of(anyShort()))). + andReturn(false).anyTimes(); + + expect(mockTopology.isAttachmentPointPort(DatapathId.of(anyLong()), + OFPort.of(anyShort()))).andReturn(true).anyTimes(); + expect(mockTopology.isConsistent(DatapathId.of(10L), OFPort.of(1), DatapathId.of(10L), OFPort.of(1))). + andReturn(true).anyTimes(); + expect(mockTopology.isConsistent(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(1))). + andReturn(true).anyTimes(); + expect(mockTopology.isConsistent(DatapathId.of(50L), OFPort.of(3), DatapathId.of(50L), OFPort.of(3))). + andReturn(true).anyTimes(); + + Date topologyUpdateTime = new Date(); + expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime). + anyTimes(); + + deviceManager.topology = mockTopology; + + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(10L), OFPort.of(1), new Date()); + Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity5 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(5L), OFPort.of(2), new Date()); + Entity entity6 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(1, 1), DatapathId.of(50L), OFPort.of(3), new Date()); + Entity entity7 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.of(50L), OFPort.of(3), new Date()); + + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener, mockTopology); + + Device d1 = deviceManager.learnDeviceByEntity(entity1); + assertSame(d1, deviceManager.learnDeviceByEntity(entity1)); + assertSame(d1, deviceManager.findDeviceByEntity(entity1)); + assertEquals(DefaultEntityClassifier.entityClass , + d1.getEntityClass()); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d1.getVlanId()); + assertArrayEquals(new IPv6Address[] { }, d1.getIPv6Addresses()); + + assertEquals(1, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener); + + Device d2 = deviceManager.learnDeviceByEntity(entity2); + assertFalse(d1.equals(d2)); + assertNotSame(d1, d2); + assertNotSame(d1.getDeviceKey(), d2.getDeviceKey()); + assertEquals(MockEntityClassifier.testEC, d2.getEntityClass()); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d2.getVlanId()); + assertArrayEquals(new IPv6Address[] { }, d2.getIPv6Addresses()); + + assertEquals(2, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceIPV6AddrChanged(isA(IDevice.class)); + replay(mockListener); + + Device d3 = deviceManager.learnDeviceByEntity(entity3); + assertNotSame(d2, d3); + assertEquals(d2.getDeviceKey(), d3.getDeviceKey()); + assertEquals(MockEntityClassifier.testEC, d3.getEntityClass()); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(1, 1) }, + d3.getIPv6Addresses()); + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) }, + d3.getAttachmentPoints()); + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) }, + d3.getAttachmentPoints(true)); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, + d3.getVlanId()); + + assertEquals(2, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceIPV6AddrChanged(isA(IDevice.class)); + replay(mockListener); + + Device d4 = deviceManager.learnDeviceByEntity(entity4); + assertNotSame(d1, d4); + assertEquals(d1.getDeviceKey(), d4.getDeviceKey()); + assertEquals(DefaultEntityClassifier.entityClass, d4.getEntityClass()); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(1, 1) }, + d4.getIPv6Addresses()); + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) }, + d4.getAttachmentPoints()); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, + d4.getVlanId()); + + assertEquals(2, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceAdded((isA(IDevice.class))); + replay(mockListener); + + Device d5 = deviceManager.learnDeviceByEntity(entity5); + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(5L), OFPort.of(2)) }, + d5.getAttachmentPoints()); + assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(4) }, + d5.getVlanId()); + assertEquals(MacAddress.of(2L), d5.getMACAddress()); + assertEquals("00:00:00:00:00:02", d5.getMACAddressString()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceAdded(isA(IDevice.class)); + replay(mockListener); + + Device d6 = deviceManager.learnDeviceByEntity(entity6); + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(50L), OFPort.of(3)) }, + d6.getAttachmentPoints()); + assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(4) }, + d6.getVlanId()); + + assertEquals(4, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + mockListener.deviceIPV6AddrChanged(isA(IDevice.class)); + replay(mockListener); + + Device d7 = deviceManager.learnDeviceByEntity(entity7); + assertNotSame(d6, d7); + assertEquals(d6.getDeviceKey(), d7.getDeviceKey()); + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(50L), OFPort.of(3)) }, + d7.getAttachmentPoints()); + assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(4) }, + d7.getVlanId()); + + assertEquals(4, deviceManager.getAllDevices().size()); + verify(mockListener); + + reset(mockListener); + replay(mockListener); + + reset(deviceManager.topology); + deviceManager.topology.addListener(deviceManager); + expectLastCall().times(1); + replay(deviceManager.topology); + + deviceManager.entityClassifier = new MockEntityClassifierMac(); + deviceManager.startUp(null); + Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.of(5, 5), DatapathId.of(-1L), OFPort.of(1), new Date()); + assertEquals(null, deviceManager.learnDeviceByEntity(entityNoClass)); + + verify(mockListener); + } private void doTestEntityOrdering(boolean computeInsertionPoint) throws Exception { - Entity e = new Entity(MacAddress.of(10L), null, null, null, null, null); + Entity e = new Entity(MacAddress.of(10L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE); IEntityClass ec = createNiceMock(IEntityClass.class); Device d = new Device(deviceManager, 1L, e, ec); @@ -528,7 +749,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { }; for (Long mac: macs) { - e = new Entity(MacAddress.of(mac), null, null, null, null, null); + e = new Entity(MacAddress.of(mac), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE); int insertionPoint; if (computeInsertionPoint) { insertionPoint = -(Arrays.binarySearch(d.entities, e)+1); @@ -600,14 +821,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.topology = mockTopology; Calendar c = Calendar.getInstance(); - Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime()); - Entity entity0 = new Entity(MacAddress.of(1L), null, null, null, null, c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity0 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, c.getTime()); c.add(Calendar.SECOND, 1); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime()); c.add(Calendar.SECOND, 1); - Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), c.getTime()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), c.getTime()); c.add(Calendar.SECOND, 1); - Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(50L), OFPort.of(1), c.getTime()); + Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(50L), OFPort.of(1), c.getTime()); IDevice d; SwitchPort[] aps; @@ -664,106 +885,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase { verify(mockListener); } - /** - * In this test, a device is moved from attachment point (1,1) to (5,1) - * and then moved back to (1,1) within 30 seconds. Both the moves should - * generate device moved notification. - * @throws Exception - */ - @Test - public void testAttachmentPointMovingBack() throws Exception { - IDeviceListener mockListener = - createMock(IDeviceListener.class); - expect(mockListener.getName()).andReturn("mockListener").atLeastOnce(); - expect(mockListener.isCallbackOrderingPostreq((String)anyObject(), (String)anyObject())) - .andReturn(false).atLeastOnce(); - expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject())) - .andReturn(false).atLeastOnce(); - - replay(mockListener); - deviceManager.addListener(mockListener); - verify(mockListener); - reset(mockListener); - - ITopologyService mockTopology = createMock(ITopologyService.class); - expect(mockTopology.getL2DomainId(DatapathId.of(1L))). - andReturn(DatapathId.of(1L)).anyTimes(); - expect(mockTopology.getL2DomainId(DatapathId.of(5L))). - andReturn(DatapathId.of(1L)).anyTimes(); - expect(mockTopology.isBroadcastDomainPort(DatapathId.of(anyLong()), OFPort.of(anyShort()))). - andReturn(false).anyTimes(); - expect(mockTopology.isInSameBroadcastDomain(DatapathId.of(anyLong()), OFPort.of(anyShort()), - DatapathId.of(anyLong()), OFPort.of(anyShort()))) - .andReturn(false).anyTimes(); - - expect(mockTopology.isAttachmentPointPort(DatapathId.of(anyLong()), - OFPort.of(anyShort()))).andReturn(true).anyTimes(); - expect(mockTopology.isConsistent(DatapathId.of(1L), OFPort.of(1), DatapathId.of(5L), OFPort.of(1))). - andReturn(false).anyTimes(); - - Date topologyUpdateTime = new Date(); - expect(mockTopology.getLastUpdateTime()).andReturn(topologyUpdateTime). - anyTimes(); - - replay(mockTopology); - - deviceManager.topology = mockTopology; - - Calendar c = Calendar.getInstance(); - Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime()); - c.add(Calendar.SECOND, 1); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime()); - c.add(Calendar.SECOND, 1); - Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime()); - c.add(Calendar.SECOND, 1); - Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime()); - - IDevice d; - SwitchPort[] aps; - - mockListener.deviceAdded(isA(IDevice.class)); - replay(mockListener); - - d = deviceManager.learnDeviceByEntity(entity1); - assertEquals(1, deviceManager.getAllDevices().size()); - aps = d.getAttachmentPoints(); - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) }, aps); - verify(mockListener); - - reset(mockListener); - mockListener.deviceMoved((isA(IDevice.class))); - replay(mockListener); - - d = deviceManager.learnDeviceByEntity(entity2); - assertEquals(1, deviceManager.getAllDevices().size()); - aps = d.getAttachmentPoints(); - - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(5L), OFPort.of(1)) }, aps); - verify(mockListener); - - reset(mockListener); - mockListener.deviceMoved((isA(IDevice.class))); - replay(mockListener); - - d = deviceManager.learnDeviceByEntity(entity3); - assertEquals(1, deviceManager.getAllDevices().size()); - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)), - new SwitchPort(DatapathId.of(5L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE)}, - d.getAttachmentPoints(true)); - verify(mockListener); - - // Generate a packet-in again from 5,1 and ensure that it doesn't - // create a device moved event. - reset(mockListener); - replay(mockListener); - d = deviceManager.learnDeviceByEntity(entity4); - assertEquals(1, deviceManager.getAllDevices().size()); - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)), - new SwitchPort(DatapathId.of(5L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE)}, - d.getAttachmentPoints(true)); - verify(mockListener); - } - private void verifyEntityArray(Entity[] expected, Device d) { Arrays.sort(expected); assertArrayEquals(expected, d.entities); @@ -818,13 +939,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.topology = mockTopology; Calendar c = Calendar.getInstance(); - Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); c.add(Calendar.SECOND, 1); - Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(2), DatapathId.of(2L), OFPort.of(1), c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(1), c.getTime()); c.add(Calendar.SECOND, 1); - Entity entity3 = new Entity(MacAddress.of(1L), null, IPv4Address.of(3), DatapathId.of(3L), OFPort.of(1), c.getTime()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(1), c.getTime()); c.add(Calendar.SECOND, 1); - Entity entity4 = new Entity(MacAddress.of(1L), null, IPv4Address.of(4), DatapathId.of(4L), OFPort.of(1), c.getTime()); + Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(4), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(1), c.getTime()); IDevice d; SwitchPort[] aps; @@ -910,7 +1031,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { reset(mockListener); ITopologyService mockTopology = createMock(ITopologyService.class); - expect(mockTopology.getL2DomainId(DatapathId.of(1L))). + expect(mockTopology.getL2DomainId(DatapathId.of(1L))). /* two different OpenFlow islands, 1 and 10 */ andReturn(DatapathId.of(1L)).anyTimes(); expect(mockTopology.getL2DomainId(DatapathId.of(5L))). andReturn(DatapathId.of(1L)).anyTimes(); @@ -941,42 +1062,41 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.addSuppressAPs(DatapathId.of(10L), OFPort.of(1)); Calendar c = Calendar.getInstance(); - Entity entity0 = new Entity(MacAddress.of(1L), null, null, null, null, c.getTime()); - // No attachment point should be learnt on 1L, 1 - Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime()); + /* OpenFlow island 1 */ + Entity entity0 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); /* suppressed */ c.add(Calendar.SECOND, 1); - Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(5L), OFPort.of(1), c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime()); c.add(Calendar.SECOND, 1); - Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), c.getTime()); + /* OpenFlow island 10 */ + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), c.getTime()); /* suppressed */ c.add(Calendar.SECOND, 1); - Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(50L), OFPort.of(1), c.getTime()); + Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(50L), OFPort.of(1), c.getTime()); IDevice d; SwitchPort[] aps; IPv4Address[] ips; + /* When the device is first added we should expect an add event */ mockListener.deviceAdded(isA(IDevice.class)); + /* It's also given an IPv4 address when the second entity is learned, so listeners should be notified */ mockListener.deviceIPV4AddrChanged((isA(IDevice.class))); replay(mockListener); - // TODO: we currently do learn entities on suppressed APs - // // cannot learn device on suppressed AP - // d = deviceManager.learnDeviceByEntity(entity1); - // assertNull(d); - + /* Device is learned w/o attachment point */ deviceManager.learnDeviceByEntity(entity0); + /* Device is "seen" with a suppressed AP now, so we shouldn't learn the AP but still keep the entity */ d = deviceManager.learnDeviceByEntity(entity1); assertEquals(1, deviceManager.getAllDevices().size()); aps = d.getAttachmentPoints(); assertEquals(aps.length, 0); - verifyEntityArray(new Entity[] { entity0, entity1} , (Device)d); + verifyEntityArray(new Entity[] { entity0 , entity1} , (Device)d); ips = d.getIPv4Addresses(); assertArrayEquals(new IPv4Address[] { IPv4Address.of(1) }, ips); verify(mockListener); reset(mockListener); mockListener.deviceMoved((isA(IDevice.class))); - //mockListener.deviceIPV4AddrChanged((isA(IDevice.class))); replay(mockListener); d = deviceManager.learnDeviceByEntity(entity2); assertEquals(1, deviceManager.getAllDevices().size()); @@ -990,7 +1110,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { reset(mockListener); replay(mockListener); - d = deviceManager.learnDeviceByEntity(entity3); + d = deviceManager.learnDeviceByEntity(entity3); assertEquals(1, deviceManager.getAllDevices().size()); aps = d.getAttachmentPoints(); assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(5L), OFPort.of(1)) }, aps); @@ -1039,13 +1159,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.topology = mockTopology; Calendar c = Calendar.getInstance(); - Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); c.add(Calendar.MILLISECOND, (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT/ 2); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime()); c.add(Calendar.MILLISECOND, (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT / 2 + 1); - Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime()); IDevice d; SwitchPort[] aps; @@ -1105,13 +1225,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.topology = mockTopology; Calendar c = Calendar.getInstance(); - Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); c.add(Calendar.MILLISECOND, (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT/ 2); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.LOCAL, c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.LOCAL, c.getTime()); c.add(Calendar.MILLISECOND, (int)AttachmentPoint.OPENFLOW_TO_EXTERNAL_TIMEOUT + 1); - Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime()); IDevice d; SwitchPort[] aps; @@ -1178,23 +1298,25 @@ public class DeviceManagerImplTest extends FloodlightTestCase { * Vlan and ip are optional all other fields must be specified. * @return */ - private static void verifyDevice(IDevice d, long mac, Short vlan, Integer ip, - long swId, int port) { + private static void verifyDevice(IDevice d, MacAddress mac, VlanVid vlan, IPv4Address ipv4, + IPv6Address ipv6, DatapathId swId, OFPort port) { assertNotNull(d); - assertEquals(MacAddress.of(mac), d.getMACAddress()); - if (vlan == null) - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d.getVlanId()); - else - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(vlan) }, d.getVlanId()); - - if (ip == null) - assertArrayEquals(new IPv4Address[] { IPv4Address.of(0) }, d.getIPv4Addresses()); - else - assertArrayEquals(new IPv4Address[] { IPv4Address.of(ip) }, d.getIPv4Addresses()); - - SwitchPort expectedAp = new SwitchPort(DatapathId.of(swId), OFPort.of(port)); - assertArrayEquals(new SwitchPort[] { expectedAp }, - d.getAttachmentPoints()); + if (!mac.equals(MacAddress.NONE)) { + assertEquals(mac, d.getMACAddress()); + } + if (vlan != null) { + assertArrayEquals(new VlanVid[] { vlan }, d.getVlanId()); + } + if (!ipv4.equals(IPv4Address.NONE)) { + assertArrayEquals(new IPv4Address[] { ipv4 }, d.getIPv4Addresses()); + } + if (!ipv6.equals(IPv6Address.NONE)) { + assertArrayEquals(new IPv6Address[] { ipv6 }, d.getIPv6Addresses()); + } + if (!swId.equals(DatapathId.NONE) && !port.equals(OFPort.ZERO)) { + SwitchPort expectedAp = new SwitchPort(swId, port); + assertArrayEquals(new SwitchPort[] { expectedAp }, d.getAttachmentPoints()); + } } @@ -1203,7 +1325,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { MacAddress deviceMac = ((Ethernet)this.testARPReplyPacket_1).getSourceMACAddress(); OFPacketIn packetIn = testARPReplyPacketIn_1; - Integer ipaddr = IPv4.toIPv4Address("192.168.1.1"); + IPv4Address ipaddr = IPv4Address.of("192.168.1.1"); // Mock up our expected behavior ITopologyService mockTopology = createMock(ITopologyService.class); @@ -1218,9 +1340,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Verify the device Device rdevice = (Device) deviceManager.findDevice(deviceMac, - VlanVid.ofVlan(5), null, null, null); - verifyDevice(rdevice, deviceMac.getLong(), - (short) 5, ipaddr, 1L, 1); + VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); IDevice cntxSrcDev = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); assertEquals(rdevice, cntxSrcDev); @@ -1230,8 +1351,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device result = null; Iterator<? extends IDevice> dstiter = - deviceManager.queryDevices(null, null, IPv4Address.of(ipaddr), - null, null); + deviceManager.queryDevices(MacAddress.NONE, null /* any VLAN here */, ipaddr, + IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); if (dstiter.hasNext()) { result = (Device)dstiter.next(); } @@ -1257,9 +1378,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Verify the device rdevice = (Device) deviceManager.findDevice(deviceMac, - VlanVid.ofVlan(5), null, null, null); - verifyDevice(rdevice, deviceMac.getLong(), - (short)5, ipaddr, 5L, 2); + VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(5), OFPort.of(2)); cntxSrcDev = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); assertEquals(rdevice, cntxSrcDev); @@ -1285,13 +1405,115 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(Command.CONTINUE, cmd); IDevice srcDev = - deviceManager.findDevice(srcMac, VlanVid.ofVlan(5), null, null, null); - verifyDevice(srcDev, srcMac.getLong(), (short)5, null, - 1L, testUDPPacketIn.getMatch().get(MatchField.IN_PORT).getShortPortNumber()); + deviceManager.findDevice(srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(srcDev, srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPPacketIn.getMatch().get(MatchField.IN_PORT)); IDevice dstDev = - deviceManager.findDevice(dstMac, VlanVid.ofVlan(5), null, null, null); - verifyDevice(dstDev, dstMac.getLong(), (short)5, ipaddr, 5L, 2); + deviceManager.findDevice(dstMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(dstDev, dstMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(5), OFPort.of(2)); + + cntxSrcDev = IDeviceService.fcStore.get(cntx, + IDeviceService.CONTEXT_SRC_DEVICE); + assertEquals(srcDev, cntxSrcDev); + + cntxDstDev = IDeviceService.fcStore.get(cntx, + IDeviceService.CONTEXT_DST_DEVICE); + assertEquals(dstDev, cntxDstDev); + + assertEquals(2, deviceManager.getAllDevices().size()); + } + + @Test + public void testPacketInBasicIPv6() throws Exception { + MacAddress deviceMac = + ((Ethernet)this.testUDPIPv6Packet).getSourceMACAddress(); + OFPacketIn packetIn = testUDPIPv6PacketIn; + IPv6Address ipaddr = IPv6Address.of(1, 1); + + // Mock up our expected behavior + ITopologyService mockTopology = createMock(ITopologyService.class); + deviceManager.topology = mockTopology; + mockTopologyForPacketInTests(mockTopology); + replay(mockTopology); + + FloodlightContext cntx = new FloodlightContext(); + Command cmd = dispatchPacketIn(1L, packetIn, cntx); + verify(mockTopology); + assertEquals(Command.CONTINUE, cmd); + // Verify the device + Device rdevice = (Device) + deviceManager.findDevice(deviceMac, + VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), IPv4Address.NONE, ipaddr, DatapathId.of(1), OFPort.of(1)); + IDevice cntxSrcDev = IDeviceService.fcStore.get(cntx, + IDeviceService.CONTEXT_SRC_DEVICE); + assertEquals(rdevice, cntxSrcDev); + IDevice cntxDstDev = IDeviceService.fcStore.get(cntx, + IDeviceService.CONTEXT_DST_DEVICE); + assertNull(cntxDstDev); + + Device result = null; + Iterator<? extends IDevice> dstiter = + deviceManager.queryDevices(MacAddress.NONE, null /* any VLAN here */, IPv4Address.NONE, + ipaddr, DatapathId.NONE, OFPort.ZERO); + if (dstiter.hasNext()) { + result = (Device)dstiter.next(); + } + assertFalse("There shouldn't be more than 1 device", dstiter.hasNext()); + assertEquals(rdevice, result); + + + //----------------- + // Test packetIn again with a different source port. Should be + // the same device + reset(mockTopology); + mockTopologyForPacketInTests(mockTopology); + replay(mockTopology); + + // trigger the packet in + cntx = new FloodlightContext(); + packetIn = packetIn.createBuilder().setMatch(OFFactories.getFactory(OFVersion.OF_13).buildMatch().setExact(MatchField.IN_PORT, OFPort.of(2)).build()).build(); + cmd = dispatchPacketIn(5L, packetIn, cntx); + verify(mockTopology); + // Verify the replay matched our expectations + assertEquals(Command.CONTINUE, cmd); + + // Verify the device + rdevice = (Device) + deviceManager.findDevice(deviceMac, + VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(rdevice, deviceMac, VlanVid.ofVlan(5), IPv4Address.NONE, ipaddr, DatapathId.of(5), OFPort.of(2)); + cntxSrcDev = IDeviceService.fcStore.get(cntx, + IDeviceService.CONTEXT_SRC_DEVICE); + assertEquals(rdevice, cntxSrcDev); + cntxDstDev = IDeviceService.fcStore.get(cntx, + IDeviceService.CONTEXT_DST_DEVICE); + assertNull(cntxDstDev); + // There can be only one device + assertEquals(1, deviceManager.getAllDevices().size()); + + //---------------------------- + // Test packetIn with a different packet going the reverse direction. + // We should now get source and dest device in the context + //==> The destination device in this step has been learned just before + MacAddress srcMac = testUDPIPv6RevPacket.getSourceMACAddress(); + MacAddress dstMac = deviceMac; + reset(mockTopology); + mockTopologyForPacketInTests(mockTopology); + replay(mockTopology); + // trigger the packet in + cntx = new FloodlightContext(); + cmd = dispatchPacketIn(1L, testUDPIPv6RevPacketIn, cntx); + verify(mockTopology); + + assertEquals(Command.CONTINUE, cmd); + IDevice srcDev = + deviceManager.findDevice(srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(srcDev, srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPIPv6PacketIn.getMatch().get(MatchField.IN_PORT)); + + IDevice dstDev = + deviceManager.findDevice(dstMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(dstDev, dstMac, VlanVid.ofVlan(5), IPv4Address.NONE, ipaddr, DatapathId.of(5), OFPort.of(2)); cntxSrcDev = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); @@ -1315,11 +1537,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { @Test public void testDeviceLearningFromArpResponseData() throws Exception { ARP arp = (ARP)((Ethernet)this.testARPReplyPacket_2).getPayload(); - MacAddress senderMac = MacAddress.of(arp.getSenderHardwareAddress()); + MacAddress senderMac = arp.getSenderHardwareAddress(); MacAddress sourceMac = ((Ethernet)this.testARPReplyPacket_2) .getSourceMACAddress(); - Integer ipaddr = IPv4.toIPv4Address("192.168.1.1"); + IPv4Address ipaddr = IPv4Address.of("192.168.1.1"); OFPacketIn packetIn = testARPReplyPacketIn_2; // Mock up our expected behavior @@ -1334,14 +1556,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase { verify(mockTopology); assertEquals(Command.CONTINUE, cmd); // Verify the device for the sender HW address - Device senderDev = (Device) - deviceManager.findDevice(senderMac, VlanVid.ofVlan(5), null, null, null); - verifyDevice(senderDev, senderMac.getLong(), (short)5, ipaddr, 1L, 1); + IDevice senderDev = (Device) + deviceManager.findDevice(senderMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); + verifyDevice(senderDev, senderMac, VlanVid.ofVlan(5), ipaddr, IPv6Address.NONE, DatapathId.of(1), OFPort.of(1)); - Device result = null; + IDevice result = null; Iterator<? extends IDevice> dstiter = - deviceManager.queryDevices(null, null, IPv4Address.of(ipaddr), - null, null); + deviceManager.queryDevices(MacAddress.NONE, null /* any VLAN here */, ipaddr, + IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); if (dstiter.hasNext()) { result = (Device)dstiter.next(); } @@ -1351,10 +1573,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Verify the device for the source MAC - Device srcDev = (Device) - deviceManager.findDevice(sourceMac, VlanVid.ofVlan(5), null, null, null); + IDevice srcDev = (Device) + deviceManager.findDevice(sourceMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); // must NOT learn IP on this device - verifyDevice(srcDev, sourceMac.getLong(), (short)5, null, 1L, 1); + verifyDevice(srcDev, sourceMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.of(1)); assertFalse("Device must differ", srcDev.equals(senderDev)); // Context is annotated with this device, not the device associated // with ARP sender address @@ -1416,8 +1638,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(Command.CONTINUE, cmd); cntxSrcDev = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); - verifyDevice(cntxSrcDev, 1L, (short)5, null, - 1L, testUDPPacketIn.getMatch().get(MatchField.IN_PORT).getShortPortNumber()); + verifyDevice(cntxSrcDev, MacAddress.of(1), VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPPacketIn.getMatch().get(MatchField.IN_PORT)); cntxDstDev = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE); @@ -1443,8 +1664,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(Command.CONTINUE, cmd); IDevice cntxSrcDev = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE); - verifyDevice(cntxSrcDev, srcMac.getLong(), (short)5, null, - 1L, testUDPPacketIn.getMatch().get(MatchField.IN_PORT).getShortPortNumber()); + verifyDevice(cntxSrcDev, srcMac, VlanVid.ofVlan(5), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1), testUDPPacketIn.getMatch().get(MatchField.IN_PORT)); IDevice cntxDstDev = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE); assertNull(cntxDstDev); @@ -1460,9 +1680,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDeviceService.CONTEXT_SRC_DEVICE); // yes: we check that cntxSrcDev matched dstMAC because we are // just adding the dest device - int ip = IPv4.toIPv4Address("192.168.1.1"); - verifyDevice(cntxSrcDev, dstMac.getLong(), (short)5, ip, - 1L, testARPReplyPacketIn_1.getMatch().get(MatchField.IN_PORT).getShortPortNumber()); + IPv4Address ip = IPv4Address.of("192.168.1.1"); + verifyDevice(cntxSrcDev, dstMac, VlanVid.ofVlan(5), ip, IPv6Address.NONE, DatapathId.of(1), testARPReplyPacketIn_1.getMatch().get(MatchField.IN_PORT)); // yes: we set the expected dst device to the current srcDev IDevice expectedDstDev = cntxSrcDev; @@ -1513,7 +1732,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { expect(mockTopology.isBroadcastDomainPort(DatapathId.of(1L), OFPort.of(1))).andReturn(false).anyTimes(); expect(mockTopology.isBroadcastDomainPort(DatapathId.of(5L), OFPort.of(1))).andReturn(false).anyTimes(); - expect(mockTopology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(mockTopology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); /* different islands */ expect(mockTopology.getL2DomainId(DatapathId.of(5L))).andReturn(DatapathId.of(5L)).anyTimes(); expect(mockTopology.isConsistent(DatapathId.of(1L), OFPort.of(1), DatapathId.of(5L), OFPort.of(1))). andReturn(false).anyTimes(); @@ -1526,23 +1745,23 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.topology = mockTopology; Calendar c = Calendar.getInstance(); - Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(2), DatapathId.of(1L), OFPort.of(1), c.getTime()); - c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1); - Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(5L), OFPort.of(1), c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); + c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1); /* go back in time s.t. entity1 is 1ms away from timing out WRT entity2 */ + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime()); deviceManager.learnDeviceByEntity(entity1); - IDevice d = deviceManager.learnDeviceByEntity(entity2); + IDevice d = deviceManager.learnDeviceByEntity(entity2); /* learn entity2 from "the past." */ assertArrayEquals(new IPv4Address[] { IPv4Address.of(1), IPv4Address.of(2) }, d.getIPv4Addresses()); assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)), new SwitchPort(DatapathId.of(5L), OFPort.of(1))}, d.getAttachmentPoints()); Iterator<? extends IDevice> diter = deviceManager.queryClassDevices(d.getEntityClass(), - null, null, IPv4Address.of(1), null, null); + MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertTrue(diter.hasNext()); assertEquals(d.getDeviceKey(), diter.next().getDeviceKey()); diter = deviceManager.queryClassDevices(d.getEntityClass(), - null, null, IPv4Address.of(2), null, null); + MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertTrue(diter.hasNext()); assertEquals(d.getDeviceKey(), diter.next().getDeviceKey()); @@ -1550,7 +1769,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.addListener(mockListener); verify(mockListener); reset(mockListener); - + mockListener.deviceMoved(isA(IDevice.class)); /* the device really should move, since it's losing an attachment point along with having its DPID changed */ mockListener.deviceIPV4AddrChanged(isA(IDevice.class)); replay(mockListener); deviceManager.entityCleanupTask.reschedule(0, null); @@ -1563,14 +1782,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase { new SwitchPort(DatapathId.of(5L), OFPort.of(1)) }, d.getAttachmentPoints()); diter = deviceManager.queryClassDevices(d.getEntityClass(), - null, null, IPv4Address.of(2), null, null); + MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertTrue(diter.hasNext()); assertEquals(d.getDeviceKey(), diter.next().getDeviceKey()); diter = deviceManager.queryClassDevices(d.getEntityClass(), - null, null, IPv4Address.of(1), null, null); + MacAddress.NONE, VlanVid.ZERO /* untagged here */, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertFalse(diter.hasNext()); - d = deviceManager.findDevice(MacAddress.of(1L), null, null, null, null); + d = deviceManager.findDevice(MacAddress.of(1L), VlanVid.ZERO /* untagged here */, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d.getIPv4Addresses()); // Attachment points are not removed, previous ones are still valid. @@ -1592,8 +1811,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Calendar c = Calendar.getInstance(); c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1); - Entity entity1 = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime()); - Entity entity2 = new Entity(MacAddress.of(1L), null, IPv4Address.of(2), DatapathId.of(5L), OFPort.of(1), c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime()); ITopologyService mockTopology = createMock(ITopologyService.class); deviceManager.topology = mockTopology; @@ -1631,10 +1850,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertNull(r); Iterator<? extends IDevice> diter = deviceManager.queryClassDevices(d.getEntityClass(), - null, null, IPv4Address.of(1), null, null); + MacAddress.NONE, VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertFalse(diter.hasNext()); - r = deviceManager.findDevice(MacAddress.of(1L), null, null, null, null); + r = deviceManager.findDevice(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertNull(r); verify(mockListener); @@ -1680,13 +1899,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Entity newEntity = new Entity (e.macAddress, e.vlan, e.ipv4Address, + e.ipv6Address, e.switchDPID, e.switchPort, e.lastSeenTimestamp); - if (e.vlan == null) + if (newEntity.vlan.equals(VlanVid.ZERO)) { newEntity.vlan = VlanVid.ofVlan(1); - else - newEntity.vlan = VlanVid.ofVlan(((e.vlan.getVlan() + 1 % 4095)+1)); + } else { + newEntity.vlan = VlanVid.ofVlan((e.vlan.getVlan() + 1 % 4095) + 1); + } newDevice = new Device(newDevice, newEntity, -1); } assertEquals(false, newDevice.equals(d)); @@ -1772,19 +1993,18 @@ public class DeviceManagerImplTest extends FloodlightTestCase { replay(mockTopology); deviceManager.topology = mockTopology; - Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime()); - Entity entity1a = new Entity(MacAddress.of(1L), null, IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), c.getTime()); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime()); - Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), c.getTime()); - entity1.setLastSeenTimestamp(c.getTime()); + /* All w/same activeSince stamp, c.getTime() */ + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity1a = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), c.getTime()); + entity1.setLastSeenTimestamp(c.getTime()); /* same as activeSince */ c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/2); - entity1a.setLastSeenTimestamp(c.getTime()); + entity1a.setLastSeenTimestamp(c.getTime()); /* halfway timed out for entity1 */ c.add(Calendar.MILLISECOND, 1); - entity2.setLastSeenTimestamp(c.getTime()); + entity2.setLastSeenTimestamp(c.getTime()); /* small increment; still below entity1 timeout */ c.add(Calendar.MILLISECOND, 1); - entity3.setLastSeenTimestamp(c.getTime()); - - + entity3.setLastSeenTimestamp(c.getTime()); /* small increment; still below entity1 timeout */ IDevice d; d = deviceManager.learnDeviceByEntity(entity1); @@ -1800,21 +2020,21 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d.getAttachmentPoints(true)); c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/4); - entity1.setLastSeenTimestamp(c.getTime()); - d = deviceManager.learnDeviceByEntity(entity1); + entity1.setLastSeenTimestamp(c.getTime()); /* entity1 now is 3/4 the way to a timeout, but still is active */ + d = deviceManager.learnDeviceByEntity(entity1); /* we're going back to an old AP here within its active window (the "flapping") */ // all are still active; entity3 should still win - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)) }, + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)) }, d.getAttachmentPoints()); - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)), + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(10L), OFPort.of(1)), new SwitchPort(DatapathId.of(5L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE), - new SwitchPort(DatapathId.of(10L), OFPort.of(1), + new SwitchPort(DatapathId.of(1L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE) }, d.getAttachmentPoints(true)); c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT+2000); - entity1.setLastSeenTimestamp(c.getTime()); + entity1.setLastSeenTimestamp(c.getTime()); /* now "relearn" entity1 for a time beyond all others' timeouts; thus entity1 should be the only remaining AP */ d = deviceManager.learnDeviceByEntity(entity1); assertEquals(entity1.getActiveSince(), entity1.getLastSeenTimestamp()); @@ -1864,10 +2084,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { replay(mockTopology); deviceManager.topology = mockTopology; - Entity entity1 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(1), c.getTime()); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(1L), OFPort.of(2), c.getTime()); - Entity entity3 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(1), c.getTime()); - Entity entity4 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(5L), OFPort.of(2), c.getTime()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), c.getTime()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), c.getTime()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), c.getTime()); + Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), c.getTime()); entity1.setLastSeenTimestamp(c.getTime()); c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT/2); c.add(Calendar.MILLISECOND, 1); @@ -1895,12 +2115,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d = deviceManager.learnDeviceByEntity(entity1); // all entities are active, so entities 2,4 should win - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)), + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(2)), new SwitchPort(DatapathId.of(5L), OFPort.of(2)) }, d.getAttachmentPoints()); - assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(1)), + assertArrayEquals(new SwitchPort[] { new SwitchPort(DatapathId.of(1L), OFPort.of(2)), new SwitchPort(DatapathId.of(5L), OFPort.of(2)), - new SwitchPort(DatapathId.of(1L), OFPort.of(2), ErrorStatus.DUPLICATE_DEVICE)}, + new SwitchPort(DatapathId.of(1L), OFPort.of(1), ErrorStatus.DUPLICATE_DEVICE)}, d.getAttachmentPoints(true)); c.add(Calendar.MILLISECOND, Entity.ACTIVITY_TIMEOUT+1); @@ -1929,11 +2149,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { } protected void doTestDeviceQuery() throws Exception { - Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date()); - Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(1), new Date()); - Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date()); - Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date()); + Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), new Date()); + Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date()); + Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date()); Device d1 = deviceManager.learnDeviceByEntity(entity1); deviceManager.learnDeviceByEntity(entity2); @@ -1943,7 +2163,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDevice d; Iterator<? extends IDevice> iter = - deviceManager.queryDevices(null, VlanVid.ofVlan(1), IPv4Address.of(1), null, null); + deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); int count = 0; while (iter.hasNext()) { count += 1; @@ -1952,7 +2172,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { } assertEquals(1, count); - iter = deviceManager.queryDevices(null, VlanVid.ofVlan(3), IPv4Address.of(3), null, null); + iter = deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); count = 0; while (iter.hasNext()) { count += 1; @@ -1961,7 +2181,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { } assertEquals(1, count); - iter = deviceManager.queryDevices(null, VlanVid.ofVlan(1), IPv4Address.of(3), null, null); + iter = deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(1), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); count = 0; while (iter.hasNext()) { count += 1; @@ -1970,7 +2190,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(0, count); Device d5 = deviceManager.learnDeviceByEntity(entity5); - iter = deviceManager.queryDevices(null, VlanVid.ofVlan(4), IPv4Address.of(3), null, null); + iter = deviceManager.queryDevices(MacAddress.NONE, VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); count = 0; Set<Long> deviceKeysFromIterator = new HashSet<Long>(); while (iter.hasNext()) { @@ -1985,7 +2205,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(2, count); - iter = deviceManager.queryDevices(MacAddress.of(1L), null, null, null, null); + iter = deviceManager.queryDevices(MacAddress.of(1L), null /* don't care (want 1 and 4) NOT VlanVid.ZERO-->untagged */, + IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); count = 0; deviceKeysFromIterator = new HashSet<Long>(); while (iter.hasNext()) { @@ -2004,7 +2225,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { public void testDeviceIndex() throws Exception { EnumSet<IDeviceService.DeviceField> indexFields = EnumSet.noneOf(IDeviceService.DeviceField.class); - indexFields.add(IDeviceService.DeviceField.IPV4); + indexFields.add(IDeviceService.DeviceField.IPv4); indexFields.add(IDeviceService.DeviceField.VLAN); deviceManager.addIndex(false, indexFields); @@ -2035,11 +2256,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { } protected void doTestDeviceClassQuery() throws Exception { - Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date()); - Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(1), new Date()); - Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date()); - Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), DatapathId.of(5L), OFPort.of(2), new Date()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date()); + Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(1), new Date()); + Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date()); + Entity entity5 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(5L), OFPort.of(2), new Date()); IDevice d1 = deviceManager.learnDeviceByEntity(entity1); IDevice d2 = deviceManager.learnDeviceByEntity(entity2); @@ -2052,8 +2273,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDevice d; Iterator<? extends IDevice> iter = - deviceManager.queryClassDevices(d1.getEntityClass(), null, - VlanVid.ofVlan(1), IPv4Address.of(1), null, null); + deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE, + VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); int count = 0; while (iter.hasNext()) { count += 1; @@ -2062,8 +2283,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { } assertEquals(1, count); - iter = deviceManager.queryClassDevices(d1.getEntityClass(), null, - VlanVid.ofVlan(3), IPv4Address.of(3), null, null); + iter = deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE, + VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); count = 0; while (iter.hasNext()) { count += 1; @@ -2073,8 +2294,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { } assertEquals(1, count); - iter = deviceManager.queryClassDevices(d1.getEntityClass(), null, - VlanVid.ofVlan(1), IPv4Address.of(3), null, null); + iter = deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE, + VlanVid.ofVlan(1), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); count = 0; while (iter.hasNext()) { count += 1; @@ -2084,8 +2305,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDevice d5 = deviceManager.learnDeviceByEntity(entity5); assertEquals(d1.getEntityClass(), d5.getEntityClass()); - iter = deviceManager.queryClassDevices(d1.getEntityClass(), null, - VlanVid.ofVlan(4), IPv4Address.of(3), null, null); + iter = deviceManager.queryClassDevices(d1.getEntityClass(), MacAddress.NONE, + VlanVid.ofVlan(4), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); count = 0; Set<Long> deviceKeysFromIterator = new HashSet<Long>(); while (iter.hasNext()) { @@ -2104,7 +2325,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { public void testDeviceClassIndex() throws Exception { EnumSet<IDeviceService.DeviceField> indexFields = EnumSet.noneOf(IDeviceService.DeviceField.class); - indexFields.add(IDeviceService.DeviceField.IPV4); + indexFields.add(IDeviceService.DeviceField.IPv4); indexFields.add(IDeviceService.DeviceField.VLAN); deviceManager.addIndex(true, indexFields); @@ -2145,14 +2366,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase { expect(mockTopology.getL2DomainId(DatapathId.of(EasyMock.anyLong()))).andReturn(DatapathId.of(1L)).anyTimes(); replay(mockTopology); - Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date()); - Entity entity2b = new Entity(MacAddress.of(22L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(1L), OFPort.of(2), new Date()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date()); + Entity entity2b = new Entity(MacAddress.of(22L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(2), new Date()); - Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(1), IPv4Address.of(3), DatapathId.of(2L), OFPort.of(1), new Date()); - Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(2), IPv4Address.of(4), DatapathId.of(2L), OFPort.of(2), new Date()); + Entity entity3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(1), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(1), new Date()); + Entity entity4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(2), IPv4Address.of(4), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date()); - Entity entity5 = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), DatapathId.of(3L), OFPort.of(1), new Date()); + Entity entity5 = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(1), new Date()); IDevice d1 = deviceManager.learnDeviceByEntity(entity1); @@ -2174,23 +2395,27 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(), entity1.getVlan(), entity1.getIpv4Address(), + entity1.getIpv6Address(), entity1.getSwitchDPID(), entity1.getSwitchPort())); // port changed. Device will be found through class index assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(), entity1.getVlan(), entity1.getIpv4Address(), + entity1.getIpv6Address(), entity1.getSwitchDPID(), OFPort.of(entity1.getSwitchPort().getPortNumber()+1))); // VLAN changed. No device matches assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(), VlanVid.ofVlan(42), entity1.getIpv4Address(), + entity1.getIpv6Address(), entity1.getSwitchDPID(), entity1.getSwitchPort())); assertEquals(null, deviceManager.findDevice(entity1.getMacAddress(), - null, + VlanVid.ZERO, entity1.getIpv4Address(), + entity1.getIpv6Address(), entity1.getSwitchDPID(), entity1.getSwitchPort())); assertEquals(d2, deviceManager.findDeviceByEntity(entity2)); @@ -2198,6 +2423,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(d3, deviceManager.findDevice(entity3.getMacAddress(), entity3.getVlan(), entity3.getIpv4Address(), + entity3.getIpv6Address(), entity3.getSwitchDPID(), entity3.getSwitchPort())); // switch and port not set. throws exception @@ -2206,8 +2432,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(null, deviceManager.findDevice(entity3.getMacAddress(), entity3.getVlan(), entity3.getIpv4Address(), - null, - null)); + entity3.getIpv6Address(), + DatapathId.NONE, + OFPort.ZERO)); } catch (IllegalArgumentException e) { exceptionCaught = true; @@ -2218,6 +2445,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(), entity5.getVlan(), entity5.getIpv4Address(), + entity5.getIpv6Address(), entity5.getSwitchDPID(), entity5.getSwitchPort())); // switch and port not set. throws exception (swith/port are key @@ -2227,8 +2455,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(d5, deviceManager.findDevice(entity5.getMacAddress(), entity5.getVlan(), entity5.getIpv4Address(), - null, - null)); + entity5.getIpv6Address(), + DatapathId.NONE, + OFPort.ZERO)); } catch (IllegalArgumentException e) { exceptionCaught = true; @@ -2237,7 +2466,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { fail("findDevice() did not throw IllegalArgumentException"); - Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), DatapathId.of(-1L), OFPort.of(1), new Date()); + Entity entityNoClass = new Entity(MacAddress.of(5L), VlanVid.ofVlan(1), IPv4Address.of(5), IPv6Address.NONE, DatapathId.of(-1L), OFPort.of(1), new Date()); assertEquals(null, deviceManager.findDeviceByEntity(entityNoClass)); @@ -2245,19 +2474,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(), entity1.getMacAddress(), entity1.getVlan(), - entity1.getIpv4Address())); + entity1.getIpv4Address(), + entity1.getIpv6Address())); assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(), entity1.getMacAddress(), entity1.getVlan(), - null)); + IPv4Address.NONE, + entity1.getIpv6Address())); assertEquals(null, deviceManager.findClassDevice(d2.getEntityClass(), entity1.getMacAddress(), - VlanVid.ofVlan(-1), - IPv4Address.of(0))); + VlanVid.ZERO, + IPv4Address.NONE, + entity1.getIpv6Address())); } - - @Test public void testGetIPv4Addresses() { // Looks like Date is only 1s granularity @@ -2285,44 +2515,44 @@ public class DeviceManagerImplTest extends FloodlightTestCase { andReturn(false).anyTimes(); replay(mockTopology); - Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), null, null, null, new Date(2000)); + Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000)); Device d1 = deviceManager.learnDeviceByEntity(e1); - assertArrayEquals(new Integer[0], d1.getIPv4Addresses()); + assertArrayEquals(new IPv4Address[0], d1.getIPv4Addresses()); - Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), null, null, new Date(2000)); + Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000)); Device d2 = deviceManager.learnDeviceByEntity(e2); d2 = deviceManager.learnDeviceByEntity(e2); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses()); // More than one entity - Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), null, DatapathId.of(2L), OFPort.of(2), new Date(3000)); + Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date(3000)); d2 = deviceManager.learnDeviceByEntity(e2b); assertEquals(2, d2.entities.length); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses()); // and now add an entity with an IP - Entity e2c = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(3), new Date(3000)); + Entity e2c = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(3), new Date(3000)); d2 = deviceManager.learnDeviceByEntity(e2c); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses()); assertEquals(3, d2.entities.length); // Other devices with different IPs shouldn't interfere - Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), null, null, new Date(4000)); - Entity e3b = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), DatapathId.of(3L), OFPort.of(3), new Date(4400)); + Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(4000)); + Entity e3b = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(3), new Date(4400)); Device d3 = deviceManager.learnDeviceByEntity(e3); d3 = deviceManager.learnDeviceByEntity(e3b); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses()); assertArrayEquals(new IPv4Address[] { IPv4Address.of(3) }, d3.getIPv4Addresses()); // Add another IP to d3 - Entity e3c = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(33), DatapathId.of(3L), OFPort.of(3), new Date(4400)); + Entity e3c = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(3L), OFPort.of(3), new Date(4400)); d3 = deviceManager.learnDeviceByEntity(e3c); IPv4Address[] ips = d3.getIPv4Addresses(); Arrays.sort(ips); assertArrayEquals(new IPv4Address[] { IPv4Address.of(3), IPv4Address.of(33) }, ips); // Add another device that also claims IP2 but is older than e2 - Entity e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), null, null, new Date(1000)); - Entity e4b = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), null, DatapathId.of(4L), OFPort.of(4), new Date(1000)); + Entity e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(1000)); + Entity e4b = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(4), new Date(1000)); Device d4 = deviceManager.learnDeviceByEntity(e4); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d2.getIPv4Addresses()); assertArrayEquals(new IPv4Address[0], d4.getIPv4Addresses()); @@ -2331,53 +2561,160 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertArrayEquals(new IPv4Address[0], d4.getIPv4Addresses()); // Make e4 and e4a newer - Entity e4c = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), null, null, new Date(5000)); - Entity e4d = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), null, DatapathId.of(4L), OFPort.of(5), new Date(5000)); + Entity e4c = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(2), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(5000)); + Entity e4d = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(5000)); d4 = deviceManager.learnDeviceByEntity(e4c); d4 = deviceManager.learnDeviceByEntity(e4d); assertArrayEquals(new IPv4Address[0], d2.getIPv4Addresses()); - // FIXME: d4 should not return IP4 assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d4.getIPv4Addresses()); // Add another newer entity to d2 but with different IP - Entity e2d = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(22), DatapathId.of(4L), OFPort.of(6), new Date(6000)); + Entity e2d = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(22), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(6), new Date(6000)); d2 = deviceManager.learnDeviceByEntity(e2d); assertArrayEquals(new IPv4Address[] { IPv4Address.of(22) }, d2.getIPv4Addresses()); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2) }, d4.getIPv4Addresses()); // new IP for d2,d4 but with same timestamp. Both devices get the IP - Entity e2e = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(42), DatapathId.of(2L), OFPort.of(4), new Date(7000)); + Entity e2e = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(42), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(4), new Date(7000)); d2 = deviceManager.learnDeviceByEntity(e2e); ips= d2.getIPv4Addresses(); Arrays.sort(ips); assertArrayEquals(new IPv4Address[] { IPv4Address.of(22), IPv4Address.of(42) }, ips); - Entity e4e = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(42), DatapathId.of(4L), OFPort.of(7), new Date(7000)); + Entity e4e = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(42), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(7), new Date(7000)); d4 = deviceManager.learnDeviceByEntity(e4e); ips= d4.getIPv4Addresses(); Arrays.sort(ips); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2), IPv4Address.of(42) }, ips); // add a couple more IPs - Entity e2f = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(4242), DatapathId.of(2L), OFPort.of(5), new Date(8000)); + Entity e2f = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(4242), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(5), new Date(8000)); d2 = deviceManager.learnDeviceByEntity(e2f); ips= d2.getIPv4Addresses(); Arrays.sort(ips); assertArrayEquals(new IPv4Address[] { IPv4Address.of(22), IPv4Address.of(42), IPv4Address.of(4242) }, ips); - Entity e4f = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(4242), DatapathId.of(4L), OFPort.of(8), new Date(9000)); + Entity e4f = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.of(4242), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(8), new Date(9000)); d4 = deviceManager.learnDeviceByEntity(e4f); ips= d4.getIPv4Addresses(); Arrays.sort(ips); assertArrayEquals(new IPv4Address[] { IPv4Address.of(2), IPv4Address.of(42), IPv4Address.of(4242) }, ips); } + + @Test + public void testGetIPv6Addresses() { + // Looks like Date is only 1s granularity + + ITopologyService mockTopology = createMock(ITopologyService.class); + deviceManager.topology = mockTopology; + expect(mockTopology.isAttachmentPointPort(DatapathId.of(anyLong()), + OFPort.of(anyShort()))). + andReturn(true).anyTimes(); + expect(mockTopology.getL2DomainId(DatapathId.of(anyLong()))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(mockTopology.isConsistent(DatapathId.of(EasyMock.anyLong()), + OFPort.of(EasyMock.anyShort()), + DatapathId.of(EasyMock.anyLong()), + OFPort.of(EasyMock.anyShort()))) + .andReturn(false) + .anyTimes(); + expect(mockTopology.isBroadcastDomainPort(DatapathId.of(EasyMock.anyLong()), + OFPort.of(EasyMock.anyShort()))) + .andReturn(false) + .anyTimes(); + expect(mockTopology.isInSameBroadcastDomain(DatapathId.of(EasyMock.anyLong()), + OFPort.of(EasyMock.anyShort()), + DatapathId.of(EasyMock.anyLong()), + OFPort.of(EasyMock.anyShort()))). + andReturn(false).anyTimes(); + replay(mockTopology); + + Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000)); + Device d1 = deviceManager.learnDeviceByEntity(e1); + assertArrayEquals(new IPv6Address[0], d1.getIPv6Addresses()); + + Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.NONE, OFPort.ZERO, new Date(2000)); + Device d2 = deviceManager.learnDeviceByEntity(e2); + d2 = deviceManager.learnDeviceByEntity(e2); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses()); + // More than one entity + Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date(3000)); + d2 = deviceManager.learnDeviceByEntity(e2b); + assertEquals(2, d2.entities.length); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses()); + // and now add an entity with an IP + Entity e2c = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.of(2L), OFPort.of(3), new Date(3000)); + d2 = deviceManager.learnDeviceByEntity(e2c); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses()); + assertEquals(3, d2.entities.length); + + // Other devices with different IPs shouldn't interfere + Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.of(3, 3), DatapathId.NONE, OFPort.ZERO, new Date(4000)); + Entity e3b = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.of(3, 3), DatapathId.of(3L), OFPort.of(3), new Date(4400)); + Device d3 = deviceManager.learnDeviceByEntity(e3); + d3 = deviceManager.learnDeviceByEntity(e3b); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses()); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(3, 3) }, d3.getIPv6Addresses()); + + // Add another IP to d3 + Entity e3c = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.of(33, 33), DatapathId.of(3L), OFPort.of(3), new Date(4400)); + d3 = deviceManager.learnDeviceByEntity(e3c); + IPv6Address[] ips = d3.getIPv6Addresses(); + Arrays.sort(ips); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(3, 3), IPv6Address.of(33, 33) }, ips); + + // Add another device that also claims IP2 but is older than e2 + Entity e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.NONE, OFPort.ZERO, new Date(1000)); + Entity e4b = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(4), new Date(1000)); + Device d4 = deviceManager.learnDeviceByEntity(e4); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d2.getIPv6Addresses()); + assertArrayEquals(new IPv6Address[0], d4.getIPv6Addresses()); + // add another entity to d4 + d4 = deviceManager.learnDeviceByEntity(e4b); + assertArrayEquals(new IPv6Address[0], d4.getIPv6Addresses()); + + // Make e4 and e4a newer + Entity e4c = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(2, 2), DatapathId.NONE, OFPort.ZERO, new Date(5000)); + Entity e4d = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(5000)); + d4 = deviceManager.learnDeviceByEntity(e4c); + d4 = deviceManager.learnDeviceByEntity(e4d); + assertArrayEquals(new IPv6Address[0], d2.getIPv6Addresses()); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d4.getIPv6Addresses()); + + // Add another newer entity to d2 but with different IP + Entity e2d = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(22, 22), DatapathId.of(4L), OFPort.of(6), new Date(6000)); + d2 = deviceManager.learnDeviceByEntity(e2d); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(22, 22) }, d2.getIPv6Addresses()); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2) }, d4.getIPv6Addresses()); + + // new IP for d2,d4 but with same timestamp. Both devices get the IP + Entity e2e = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(42, 42), DatapathId.of(2L), OFPort.of(4), new Date(7000)); + d2 = deviceManager.learnDeviceByEntity(e2e); + ips= d2.getIPv6Addresses(); + Arrays.sort(ips); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(22, 22), IPv6Address.of(42, 42) }, ips); + Entity e4e = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(42, 42), DatapathId.of(4L), OFPort.of(7), new Date(7000)); + d4 = deviceManager.learnDeviceByEntity(e4e); + ips= d4.getIPv6Addresses(); + Arrays.sort(ips); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2), IPv6Address.of(42, 42) }, ips); + + // add a couple more IPs + Entity e2f = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.of(4242, 4242), DatapathId.of(2L), OFPort.of(5), new Date(8000)); + d2 = deviceManager.learnDeviceByEntity(e2f); + ips= d2.getIPv6Addresses(); + Arrays.sort(ips); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(22, 22), IPv6Address.of(42, 42), IPv6Address.of(4242, 4242) }, ips); + Entity e4f = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.of(4242, 4242), DatapathId.of(4L), OFPort.of(8), new Date(9000)); + d4 = deviceManager.learnDeviceByEntity(e4f); + ips= d4.getIPv6Addresses(); + Arrays.sort(ips); + assertArrayEquals(new IPv6Address[] { IPv6Address.of(2, 2), IPv6Address.of(42, 42), IPv6Address.of(4242, 4242) }, ips); + } - // TODO: this test should really go into a separate class that collects - // unit tests for Device @Test public void testGetSwitchPortVlanId() { - Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), null, DatapathId.of(10L), OFPort.of(1), new Date()); - Entity entity2 = new Entity(MacAddress.of(1L), null, null, DatapathId.of(10L), OFPort.of(1), new Date()); - Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(3), null, DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(42), null, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date()); + Entity entity2 = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(10L), OFPort.of(1), new Date()); + Entity entity3 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity4 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(42), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); Entity[] entities = new Entity[] { entity1, entity2, entity3, entity4 }; @@ -2387,7 +2724,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { SwitchPort swp1x2 = new SwitchPort(DatapathId.of(1L), OFPort.of(2)); SwitchPort swp2x1 = new SwitchPort(DatapathId.of(2L), OFPort.of(1)); SwitchPort swp10x1 = new SwitchPort(DatapathId.of(10L), OFPort.of(1)); - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1), VlanVid.ofVlan(1)}, + assertArrayEquals(new VlanVid[] { VlanVid.ZERO, VlanVid.ofVlan(1)}, d.getSwitchPortVlanIds(swp10x1)); assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(3), VlanVid.ofVlan(42)}, d.getSwitchPortVlanIds(swp1x1)); @@ -2424,10 +2761,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { //flexClassifier.createTestEntityClass("Class1"); - Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); - Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(1), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(2), new Date()); - Entity entity2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(2), new Date()); + Entity entity1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), new Date()); + Entity entity2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(1), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date()); + Entity entity2b = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.NONE, DatapathId.of(2L), OFPort.of(2), new Date()); Device d1 = deviceManager.learnDeviceByEntity(entity1); @@ -2436,20 +2773,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device d2b = deviceManager.learnDeviceByEntity(entity2b); d1 = deviceManager.getDeviceIteratorForQuery(entity1.getMacAddress(), - entity1.getVlan(), entity1.getIpv4Address(), + entity1.getVlan(), entity1.getIpv4Address(), entity1.getIpv6Address(), entity1.getSwitchDPID(), entity1.getSwitchPort()) .next(); d1b = deviceManager.getDeviceIteratorForQuery(entity1b.getMacAddress(), - entity1b.getVlan(), entity1b.getIpv4Address(), + entity1b.getVlan(), entity1b.getIpv4Address(), entity1b.getIpv6Address(), entity1b.getSwitchDPID(), entity1b.getSwitchPort()).next(); assertEquals(d1, d1b); d2 = deviceManager.getDeviceIteratorForQuery(entity2.getMacAddress(), - entity2.getVlan(), entity2.getIpv4Address(), + entity2.getVlan(), entity2.getIpv4Address(), entity2.getIpv6Address(), entity2.getSwitchDPID(), entity2.getSwitchPort()).next(); d2b = deviceManager.getDeviceIteratorForQuery(entity2b.getMacAddress(), - entity2b.getVlan(), entity2b.getIpv4Address(), + entity2b.getVlan(), entity2b.getIpv4Address(), entity2b.getIpv6Address(), entity2b.getSwitchDPID(), entity2b.getSwitchPort()).next(); assertEquals(d2, d2b); @@ -2520,8 +2857,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { @Test public void testSyncEntity() { Date d1 = new Date(); - Date d2 = new Date(0); - Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), d1); + Date d2 = new Date(1); + Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), d1); e1.setActiveSince(d2); SyncEntity se1 = new SyncEntity(e1); assertEntityEquals(e1, se1); @@ -2535,7 +2872,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertNotSame(d1, se1.lastSeenTimestamp); assertNotSame(d2, se1.activeSince); - Entity e2 = new Entity(MacAddress.of(42L), null, null, null, null, null); + Entity e2 = new Entity(MacAddress.of(42L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE); SyncEntity se2 = new SyncEntity(e2); assertEntityEquals(e2, se2); @@ -2564,8 +2901,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals("MyKey", dsr.toString()); List<SyncEntity> entities = new ArrayList<SyncEntity>(); - Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000)); - Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), null, DatapathId.of(4L), OFPort.of(5), new Date(0)); + Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000)); + Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1)); entities.add(new SyncEntity(e1a)); entities.add(new SyncEntity(e1b)); // e1b comes before e1 (lastSeen) but we add it after it to test @@ -2593,7 +2930,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { //************************************** // Test 1: a single entity - Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000)); + Entity e1 = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000)); Device d1 = deviceManager.learnDeviceByEntity(e1); assertEquals("Sanity check failed. Device doesn't have the expected " + "entity class. Something with the test setup is strange", @@ -2614,7 +2951,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Test 1b: same device, now with a second entity (no IP). // this second entity has a lastSeen time that is earlier than the // first entity - Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), null, DatapathId.of(4L), OFPort.of(5), new Date(0)); + Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1)); d1 = deviceManager.learnDeviceByEntity(e1b); assertEquals("Sanity check failed. Should still be same device but " + "deviceKeys differs", deviceKey, d1.getDeviceKey()); @@ -2630,7 +2967,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { //************************************** // Test 1c: same device with a third entity that does not have a // switch port. It should be added to the DeviceSyncRepresentation - Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), null, null, new Date(2000)); + Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date(2000)); d1 = deviceManager.learnDeviceByEntity(e1c); assertEquals("Sanity check failed. Should still be same device but " + "deviceKeys differs", deviceKey, d1.getDeviceKey()); @@ -2648,7 +2985,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // attachment point and that is newer. Device should move and // non-attachment point entities should be removed (e1b). Although // e1 is non-attachment point it will remain because it has an IP - Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), DatapathId.of(4L), OFPort.of(6), new Date(3000)); + Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(6), new Date(3000)); d1 = deviceManager.learnDeviceByEntity(e1d); assertEquals("Sanity check failed. Should still be same device but " + "deviceKeys differs", deviceKey, d1.getDeviceKey()); @@ -2662,12 +2999,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d1 = null; - //************************************** // Test 2: a second device with a different entity class. The // mock entity classifier will return an entity class where all // fields are keys if the DPID is > 10L - Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(23), IPv4Address.of(24), DatapathId.of(11L), OFPort.of(1), new Date(0)); + Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(23), IPv4Address.of(24), IPv6Address.NONE, DatapathId.of(11L), OFPort.of(1), new Date(1)); Device d2 = deviceManager.learnDeviceByEntity(e2); DeviceSyncRepresentation dsr2 = new DeviceSyncRepresentation(d2); assertEquals("Sanity check failed. Device doesn't have the expected " + @@ -2740,8 +3076,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.topology = mockTopology; deviceManager.setSyncStoreWriteInterval(syncStoreIntervalMs); - Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000)); - e1a.setActiveSince(new Date(0)); + Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000)); + e1a.setActiveSince(new Date(1)); deviceManager.learnDeviceByEntity(e1a); //storeClient.put("FooBar", new DeviceSyncRepresentation()); @@ -2754,8 +3090,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Same entity but newer timestamp. Since the device hasn't changed, // only the timestamp is updated and the write should be throttled. - Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(2000)); - e1b.setActiveSince(new Date(0)); + Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000)); + e1b.setActiveSince(new Date(1)); /* cannot use Date(0), since that's our 'no date' value now */ deviceManager.learnDeviceByEntity(e1a); entries = getEntriesFromStore(); assertEquals(1, entries.size()); @@ -2765,8 +3101,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Wait for the write interval to expire then write again. Thread.sleep(syncStoreIntervalMs+5); - Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(3000)); - e1c.setActiveSince(new Date(0)); + Entity e1c = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(3000)); + e1c.setActiveSince(new Date(1)); deviceManager.learnDeviceByEntity(e1c); entries = getEntriesFromStore(); assertEquals(1, entries.size()); @@ -2776,8 +3112,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Entity for same device but with different IP. should be added // immediately - Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), DatapathId.of(4L), OFPort.of(5), new Date(4000)); - e1d.setActiveSince(new Date(0)); + Entity e1d = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(4000)); + e1d.setActiveSince(new Date(1)); deviceManager.learnDeviceByEntity(e1d); entries = getEntriesFromStore(); assertEquals(1, entries.size()); @@ -2790,8 +3126,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // update immediately without throttle. // Note: the previous entities will still be there because they have // IPs (even though they aren't for the current attachment point) - Entity e1e = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), DatapathId.of(4L), OFPort.of(6), new Date(5000)); - e1e.setActiveSince(new Date(0)); + Entity e1e = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(6), new Date(5000)); + e1e.setActiveSince(new Date(1)); deviceManager.learnDeviceByEntity(e1e); entries = getEntriesFromStore(); assertEquals(1, entries.size()); @@ -2802,7 +3138,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEntityEquals(e1e, dsr1.getEntities().get(2)); // Add a second device - Entity e2 = new Entity(MacAddress.of(2L), null, null, DatapathId.of(5L), OFPort.of(5), new Date()); + Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(5), new Date()); deviceManager.learnDeviceByEntity(e2); entries = getEntriesFromStore(); assertEquals(2, entries.size()); @@ -2852,7 +3188,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { private IDevice getSingleDeviceFromDeviceManager(long mac) { Iterator<? extends IDevice> diter = - deviceManager.queryDevices(MacAddress.of(mac), null, null, null, null); + deviceManager.queryDevices(MacAddress.of(mac), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertTrue("Query didn't return a device", diter.hasNext()); IDevice d = diter.next(); assertFalse("Query returned more than one device", diter.hasNext()); @@ -2872,8 +3208,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.setInitialSyncStoreConsolidateMs(initialSyncStoreConsolidateIntervalMs); // Add Device1 with two entities with two different IPs - Entity e1a = new Entity(MacAddress.of(1L), null, IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(1000)); - Entity e1b = new Entity(MacAddress.of(1L), null, IPv4Address.of(33), DatapathId.of(4L), OFPort.of(5), new Date(2000)); + Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000)); + Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(33), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000)); Device d1 = deviceManager.allocateDevice(1L, e1a, DefaultEntityClassifier.entityClass); d1 = deviceManager.allocateDevice(d1, e1b, -1); @@ -2882,8 +3218,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Add Device2 with different switch-ports. Only the most recent // one should be the attachment point - Entity e2a = new Entity(MacAddress.of(2L), null, null, DatapathId.of(4L), OFPort.of(4), new Date(1000)); - Entity e2b = new Entity(MacAddress.of(2L), null, null, DatapathId.of(4L), OFPort.of(5), new Date(2000)); + Entity e2a = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(4), new Date(1000)); + Entity e2b = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000)); Device d2 = deviceManager.allocateDevice(2L, e2a, DefaultEntityClassifier.entityClass); d2 = deviceManager.allocateDevice(d2, e2b, -1); @@ -2909,15 +3245,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Query for the Device1. Make sure we have the two IPs we stored. IDevice d = getSingleDeviceFromDeviceManager(1L); assertDeviceIps(new IPv4Address[] {IPv4Address.of(3), IPv4Address.of(33)}, d); - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d.getVlanId()); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d.getVlanId()); swp = new SwitchPort(DatapathId.of(4L), OFPort.of(5)); assertArrayEquals(new SwitchPort[] { swp }, d.getAttachmentPoints()); // Query for Device2. Make sure we only have the more recent AP // Query for the Device1. Make sure we have the two IPs we stored. d = getSingleDeviceFromDeviceManager(2L); - assertArrayEquals(new Integer[0], d.getIPv4Addresses()); - assertArrayEquals(new VlanVid[] { VlanVid.ofVlan(-1) }, d.getVlanId()); + assertArrayEquals(new IPv4Address[0], d.getIPv4Addresses()); + assertArrayEquals(new VlanVid[] { VlanVid.ZERO }, d.getVlanId()); swp = new SwitchPort(DatapathId.of(4L), OFPort.of(5)); assertArrayEquals(new SwitchPort[] { swp }, d.getAttachmentPoints()); @@ -2925,7 +3261,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // add another entry device to the store. since device manager is // already master we won't read this device and it should be // removed from the store by the consolidate task - Entity e3 = new Entity(MacAddress.of(3L), null, null, DatapathId.of(1L), OFPort.of(1), null); + Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(1L), OFPort.of(1), Entity.NO_DATE); dsr = new DeviceSyncRepresentation(); dsr.setKey("Device3"); dsr.setEntities(Collections.singletonList(new SyncEntity(e3))); @@ -2941,7 +3277,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertTrue("Device3 not in store. Entries in store: " + entries, found); // make sure it's not in DevManager Iterator<? extends IDevice> diter = - deviceManager.queryDevices(MacAddress.of(3L), null, null, null, null); + deviceManager.queryDevices(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertFalse("Device3 found in DeviceManager. Should be there", diter.hasNext()); @@ -2957,7 +3293,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertFalse("Device3 not is still in the store. Entries in store: " + entries, found); // make sure it's not in DevManager - diter = deviceManager.queryDevices(MacAddress.of(3L), null, null, null, null); + diter = deviceManager.queryDevices(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); assertFalse("Device3 found in DeviceManager. Should be there", diter.hasNext()); } @@ -2975,15 +3311,15 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Add Device1 with two entities to store and let device manager // learn - Entity e1a = new Entity(MacAddress.of(1L), null, null, DatapathId.of(4L), OFPort.of(5), new Date(1000)); - Entity e1b = new Entity(MacAddress.of(1L), null, IPv4Address.of(3), DatapathId.of(4L), OFPort.of(5), new Date(2000)); + Entity e1a = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(1000)); + Entity e1b = new Entity(MacAddress.of(1L), VlanVid.ZERO, IPv4Address.of(3), IPv6Address.NONE, DatapathId.of(4L), OFPort.of(5), new Date(2000)); Device d1 = deviceManager.learnDeviceByEntity(e1a); deviceManager.learnDeviceByEntity(e1b); String dev1Key = DeviceSyncRepresentation.computeKey(d1); // Add a second device to the store but do NOT add to device manager - Entity e2 = new Entity(MacAddress.of(2L), null, null, DatapathId.of(5L), OFPort.of(5), new Date()); + Entity e2 = new Entity(MacAddress.of(2L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(5L), OFPort.of(5), new Date()); Device d2 = deviceManager.allocateDevice(42L, e2, DefaultEntityClassifier.entityClass); DeviceSyncRepresentation dsr = new DeviceSyncRepresentation(d2); @@ -3022,7 +3358,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Now write a device to the store that doesn't have any switch-port // it should be removed - Entity e3 = new Entity(MacAddress.of(3L), null, null, null, null, null); + Entity e3 = new Entity(MacAddress.of(3L), VlanVid.ZERO, IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE); dsr.setKey("Device3"); dsr.setEntities(Collections.singletonList(new SyncEntity(e3))); storeClient.put(dsr.getKey(), dsr); @@ -3033,7 +3369,5 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Thread.sleep(25); // give the scheduler time to run the task versioned = storeClient.get("Device3"); assertNull(versioned.getValue()); - } - -} +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java index e820f9e082e1ce9aa4c0686f7b87f05c34d88897..47590faaaca6c85e9f0086a3ea491920cc1cbdb1 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java @@ -28,6 +28,7 @@ import java.util.Iterator; import org.junit.Test; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -52,8 +53,8 @@ public class DeviceUniqueIndexTest extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); - e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), DatapathId.of(1L), OFPort.of(1), new Date()); + e1a = new Entity(MacAddress.of(1L), VlanVid.ofVlan(1), IPv4Address.of(1), IPv6Address.of(1, 1), DatapathId.of(1L), OFPort.of(1), new Date()); + e1b = new Entity(MacAddress.of(1L), VlanVid.ofVlan(2), IPv4Address.of(1), IPv6Address.of(1, 1), DatapathId.of(1L), OFPort.of(1), new Date()); List<Entity> d1Entities = new ArrayList<Entity>(2); d1Entities.add(e1a); d1Entities.add(e1b); @@ -61,14 +62,14 @@ public class DeviceUniqueIndexTest extends TestCase { d1Entities, null); // e2 and e2 alt match in MAC and VLAN - e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), DatapathId.of(2L), OFPort.of(2), new Date()); - e2alt = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), null, null, null, null); + e2 = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.of(2), IPv6Address.of(2, 2), DatapathId.of(2L), OFPort.of(2), new Date()); + e2alt = new Entity(MacAddress.of(2L), VlanVid.ofVlan(2), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, Entity.NO_DATE); // IP is null - e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), null, DatapathId.of(3L), OFPort.of(3), new Date()); + e3 = new Entity(MacAddress.of(3L), VlanVid.ofVlan(3), IPv4Address.NONE, IPv6Address.NONE, DatapathId.of(3L), OFPort.of(3), new Date()); // IP and switch and port are null - e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), null, null, null, new Date()); + e4 = new Entity(MacAddress.of(4L), VlanVid.ofVlan(4), IPv4Address.NONE, IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO, new Date()); } /* @@ -142,7 +143,8 @@ public class DeviceUniqueIndexTest extends TestCase { //------------- // Test null keys DeviceUniqueIndex idx2 = new DeviceUniqueIndex( - EnumSet.of(DeviceField.IPV4, + EnumSet.of(DeviceField.IPv4, + DeviceField.IPv6, DeviceField.SWITCH)); // only one key field is null idx2.updateIndex(e3, 3L); @@ -172,4 +174,4 @@ public class DeviceUniqueIndexTest extends TestCase { // anyways. We can now add d1 ;-) assertEquals(true, idx3.updateIndex(d1, 1L)); } -} +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java index f8c0bc3f082159a81fd6cdbfb0761ebf57182a00..0feff29e737ad5479c4ee9fd849828df7f4918a0 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java @@ -23,7 +23,10 @@ import java.util.Collection; import java.util.List; import java.util.TreeSet; +import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.OFPort; import net.floodlightcontroller.devicemanager.IEntityClass; import net.floodlightcontroller.devicemanager.SwitchPort; @@ -62,20 +65,31 @@ public class MockDevice extends Device { public IPv4Address[] getIPv4Addresses() { TreeSet<IPv4Address> vals = new TreeSet<IPv4Address>(); for (Entity e : entities) { - if (e.getIpv4Address() == null) continue; + if (e.getIpv4Address().equals(IPv4Address.NONE)) continue; vals.add(e.getIpv4Address()); } return vals.toArray(new IPv4Address[vals.size()]); } + + @Override + public IPv6Address[] getIPv6Addresses() { + TreeSet<IPv6Address> vals = new TreeSet<IPv6Address>(); + for (Entity e : entities) { + if (e.getIpv6Address().equals(IPv6Address.NONE)) continue; + vals.add(e.getIpv6Address()); + } + + return vals.toArray(new IPv6Address[vals.size()]); + } @Override public SwitchPort[] getAttachmentPoints() { ArrayList<SwitchPort> vals = new ArrayList<SwitchPort>(entities.length); for (Entity e : entities) { - if (e.getSwitchDPID() != null && - e.getSwitchPort() != null && + if (!e.getSwitchDPID().equals(DatapathId.NONE) && + !e.getSwitchPort().equals(OFPort.ZERO) && deviceManager.isValidAttachmentPoint(e.getSwitchDPID(), e.getSwitchPort())) { SwitchPort sp = new SwitchPort(e.getSwitchDPID(), e.getSwitchPort()); @@ -90,5 +104,4 @@ public class MockDevice extends Device { return "MockDevice [getEntityClass()=" + getEntityClass() + ", getEntities()=" + Arrays.toString(getEntities()) + "]"; } - -} +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java index 780e7eb8166383f1cbcf27e1aa3f5f5bdc5a87bd..33efb3253cc67dde0fb16bca1603ad3538d56e81 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java @@ -22,6 +22,7 @@ import java.util.List; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.VlanVid; @@ -62,56 +63,27 @@ public class MockDeviceManager extends DeviceManagerImpl { /** * Learn a device using the given characteristics. * @param macAddress the MAC - * @param vlan the VLAN (can be null) - * @param ipv4Address the IP (can be null) - * @param switchDPID the attachment point switch DPID (can be null) - * @param switchPort the attachment point switch port (can be null) + * @param vlan the VLAN (can be VlanVid.ZERO for untagged) + * @param ipv4Address the IPv4 (can be IPv4Address.NONE) + * @param ipv6Address the IPv6 (can be IPv6Address.NONE) + * @param switchDPID the attachment point switch DPID (can be DatapathId.NONE) + * @param switchPort the attachment point switch port (can be OFPort.ZERO) * @param processUpdates if false, will not send updates. Note that this * method is not thread safe if this is false * @return the device, either new or not */ - public IDevice learnEntity(long macAddress, Short vlan, - Integer ipv4Address, Long switchDPID, - Integer switchPort, + public IDevice learnEntity(MacAddress macAddress, VlanVid vlan, + IPv4Address ipv4Address, IPv6Address ipv6Address, DatapathId switchDPID, + OFPort switchPort, boolean processUpdates) { List<IDeviceListener> listeners = deviceListeners.getOrderedListeners(); if (!processUpdates) { deviceListeners.clearListeners(); } - VlanVid v; - IPv4Address i; - DatapathId d; - OFPort p; - - if (vlan != null && vlan.shortValue() <= 0) - vlan = null; - if (ipv4Address != null && ipv4Address == 0) - ipv4Address = null; - - if (vlan == null) { - v = VlanVid.ofVlan(-1); - } else { - v = VlanVid.ofVlan(vlan); - } - if (ipv4Address == null) { - i = IPv4Address.NONE; - } else { - i = IPv4Address.of(ipv4Address); - } - if (switchDPID == null) { - d = DatapathId.of(0); - } else { - d = DatapathId.of(switchDPID.longValue()); - } - if (switchPort == null) { - p = OFPort.ZERO; - } else { - p = OFPort.of(switchPort); - } - - IDevice res = learnDeviceByEntity(new Entity(MacAddress.of(macAddress), - v, i, d, p, new Date())); + /* Entity will enforce all but VLAN be non-null */ + IDevice res = learnDeviceByEntity(new Entity(macAddress, + vlan, ipv4Address, ipv6Address, switchDPID, switchPort, new Date())); // Restore listeners if (listeners != null) { for (IDeviceListener listener : listeners) { @@ -129,17 +101,17 @@ public class MockDeviceManager extends DeviceManagerImpl { /** * Learn a device using the given characteristics. * @param macAddress the MAC - * @param vlan the VLAN (can be null) - * @param ipv4Address the IP (can be null) - * @param switchDPID the attachment point switch DPID (can be null) - * @param switchPort the attachment point switch port (can be null) + * @param vlan the VLAN (can be VlanVid.ZERO for untagged) + * @param ipv4Address the IPv4 (can be IPv4Address.NONE) + * @param ipv6Address the IPv6 (can be IPv6Address.NONE) + * @param switchDPID the attachment point switch DPID (can be DatapathId.NONE) + * @param switchPort the attachment point switch port (can be OFPort.ZERO) * @return the device, either new or not */ - public IDevice learnEntity(long macAddress, Short vlan, - Integer ipv4Address, Long switchDPID, - Integer switchPort) { - return learnEntity(macAddress, vlan, ipv4Address, - switchDPID, switchPort, true); + public IDevice learnEntity(MacAddress macAddress, VlanVid vlan, + IPv4Address ipv4Address, IPv6Address ipv6Address, DatapathId switchDPID, + OFPort switchPort) { + return learnEntity(macAddress, vlan, ipv4Address, ipv6Address, switchDPID, switchPort, true); } @Override @@ -171,4 +143,4 @@ public class MockDeviceManager extends DeviceManagerImpl { super.init(fmc); setSyncServiceIfNotSet(new MockSyncService()); } -} +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java index 2d92357f453f21c2bf8cc5a8543168a65dd695e0..009369e7f55c5810308bc06324a1a0a7943a2237 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifier.java @@ -59,5 +59,4 @@ public class MockEntityClassifier extends DefaultEntityClassifier { public EnumSet<IDeviceService.DeviceField> getKeyFields() { return EnumSet.of(MAC, VLAN, SWITCH, PORT); } - } \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java index a953d5c14dfc0b69c9a899810e37c453d087d636..edc25d9d890f8670d351a9aaaceed333a43750e7 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockEntityClassifierMac.java @@ -23,6 +23,8 @@ import static net.floodlightcontroller.devicemanager.IDeviceService.DeviceField. import java.util.EnumSet; +import org.projectfloodlight.openflow.types.DatapathId; + import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.IEntityClass; import net.floodlightcontroller.devicemanager.IDeviceService.DeviceField; @@ -59,11 +61,11 @@ public class MockEntityClassifierMac extends DefaultEntityClassifier { if (entity.getSwitchDPID() == null) { throw new IllegalArgumentException("Not all key fields specified." + " Required fields: " + getKeyFields()); - } else if (entity.getSwitchDPID().getLong() == 1L) { + } else if (entity.getSwitchDPID().equals(DatapathId.of(1))) { return testECMac1; - } else if (entity.getSwitchDPID().getLong() == 2L) { + } else if (entity.getSwitchDPID().equals(DatapathId.of(2))) { return testECMac2; - } else if (entity.getSwitchDPID().getLong() == -1L) { + } else if (entity.getSwitchDPID().equals(DatapathId.of(-1))) { return null; } return DefaultEntityClassifier.entityClass; diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java index c02e34069e4130f6fb0a9c2466f7b59b4ca7a440..fbb17e36ba5b0a17228da3b9f85eda8a6e11c607 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockFlexEntityClassifier.java @@ -89,4 +89,4 @@ public class MockFlexEntityClassifier extends DefaultEntityClassifier { public EnumSet<IDeviceService.DeviceField> getKeyFields() { return EnumSet.of(MAC, VLAN, SWITCH, PORT); } -} +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java b/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java index 1f5790736d88f532686feb46090ababa8ad37878..7cc6ef19fd40e641721125a9e5e093b318259453 100644 --- a/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java +++ b/src/test/java/net/floodlightcontroller/firewall/FirewallTest.java @@ -57,6 +57,7 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.protocol.match.MatchField; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.IPv4Address; import org.projectfloodlight.openflow.types.IPv4AddressWithMask; import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; @@ -151,10 +152,10 @@ public class FirewallTest extends FloodlightTestCase { .setOpCode(ARP.OP_REQUEST) .setHardwareAddressLength((byte)6) .setProtocolAddressLength((byte)4) - .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00")) - .setSenderProtocolAddress(IPv4.toIPv4Address("192.168.1.1")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:00:00:00:00:00")) - .setTargetProtocolAddress(IPv4.toIPv4Address("192.168.1.2")) + .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:00")) + .setSenderProtocolAddress(IPv4Address.of("192.168.1.1")) + .setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:00")) + .setTargetProtocolAddress(IPv4Address.of("192.168.1.2")) .setPayload(new Data(new byte[] {0x01}))); // Build a ARP packet @@ -170,10 +171,10 @@ public class FirewallTest extends FloodlightTestCase { .setOpCode(ARP.OP_REQUEST) .setHardwareAddressLength((byte)6) .setProtocolAddressLength((byte)4) - .setSenderHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) - .setSenderProtocolAddress(IPv4.toIPv4Address("192.168.1.2")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:00")) - .setTargetProtocolAddress(IPv4.toIPv4Address("192.168.1.1")) + .setSenderHardwareAddress(MacAddress.of("00:11:22:33:44:55")) + .setSenderProtocolAddress(IPv4Address.of("192.168.1.2")) + .setTargetHardwareAddress(MacAddress.of("00:44:33:22:11:00")) + .setTargetProtocolAddress(IPv4Address.of("192.168.1.1")) .setPayload(new Data(new byte[] {0x01}))); // Build a broadcast IP packet diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 45eac76cfb4577227f6cc6cd3bf1ae787aa0c407..51f1faefb541b21acefbe0151385aa1a7277ad9f 100644 --- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java +++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java @@ -1,19 +1,19 @@ /** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ + * Copyright 2011, Big Switch Networks, Inc. + * Originally created by David Erickson, Stanford University + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ package net.floodlightcontroller.forwarding; @@ -45,6 +45,7 @@ import net.floodlightcontroller.packet.Data; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.IPv6; import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.Route; @@ -73,12 +74,14 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.IpProtocol; 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.VlanVid; import org.projectfloodlight.openflow.protocol.OFPacketInReason; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.protocol.action.OFActionOutput; @@ -86,435 +89,697 @@ import org.sdnplatform.sync.ISyncService; import org.sdnplatform.sync.test.MockSyncService; public class ForwardingTest extends FloodlightTestCase { - protected FloodlightContext cntx; - protected MockDeviceManager deviceManager; - protected IRoutingService routingEngine; - protected Forwarding forwarding; - protected ITopologyService topology; - protected MockThreadPoolService threadPool; - protected IOFSwitch sw1, sw2; - protected OFFeaturesReply swFeatures; - protected IDevice srcDevice, dstDevice1, dstDevice2; - protected OFPacketIn packetIn; - protected OFPacketOut packetOut; - protected OFPacketOut packetOutFlooded; - protected IPacket testPacket; - protected byte[] testPacketSerialized; - protected int expected_wildcards; - protected Date currentDate; - private MockSyncService mockSyncService; - private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); - - - @Override - public void setUp() throws Exception { - super.setUp(); - - cntx = new FloodlightContext(); - - // Module loader setup - /* - Collection<Class<? extends IFloodlightModule>> mods = new ArrayList<Class<? extends IFloodlightModule>>(); - Collection<IFloodlightService> mockedServices = new ArrayList<IFloodlightService>(); - mods.add(Forwarding.class); - routingEngine = createMock(IRoutingService.class); - topology = createMock(ITopologyService.class); - mockedServices.add(routingEngine); - mockedServices.add(topology); - FloodlightTestModuleLoader fml = new FloodlightTestModuleLoader(); - fml.setupModules(mods, mockedServices); - mockFloodlightProvider = - (MockFloodlightProvider) fml.getModuleByName(MockFloodlightProvider.class); - deviceManager = - (MockDeviceManager) fml.getModuleByName(MockDeviceManager.class); - threadPool = - (MockThreadPoolService) fml.getModuleByName(MockThreadPoolService.class); - forwarding = - (Forwarding) fml.getModuleByName(Forwarding.class); - */ - mockFloodlightProvider = getMockFloodlightProvider(); - forwarding = new Forwarding(); - threadPool = new MockThreadPoolService(); - deviceManager = new MockDeviceManager(); - routingEngine = createMock(IRoutingService.class); - topology = createMock(ITopologyService.class); - mockSyncService = new MockSyncService(); - DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier(); - - - FloodlightModuleContext fmc = new FloodlightModuleContext(); - fmc.addService(IFloodlightProviderService.class, - mockFloodlightProvider); - fmc.addService(IThreadPoolService.class, threadPool); - fmc.addService(ITopologyService.class, topology); - fmc.addService(IRoutingService.class, routingEngine); - fmc.addService(IDeviceService.class, deviceManager); - fmc.addService(IEntityClassifierService.class, entityClassifier); - fmc.addService(ISyncService.class, mockSyncService); - fmc.addService(IDebugCounterService.class, new MockDebugCounterService()); - fmc.addService(IDebugEventService.class, new MockDebugEventService()); - fmc.addService(IOFSwitchService.class, getMockSwitchService()); - - topology.addListener(anyObject(ITopologyListener.class)); - expectLastCall().anyTimes(); - expect(topology.isIncomingBroadcastAllowed(anyObject(DatapathId.class), anyObject(OFPort.class))).andReturn(true).anyTimes(); - replay(topology); - - threadPool.init(fmc); - mockSyncService.init(fmc); - forwarding.init(fmc); - deviceManager.init(fmc); - entityClassifier.init(fmc); - threadPool.startUp(fmc); - mockSyncService.startUp(fmc); - deviceManager.startUp(fmc); - forwarding.startUp(fmc); - entityClassifier.startUp(fmc); - verify(topology); - - swFeatures = factory.buildFeaturesReply().setNBuffers(1000).build(); - // Mock switches - sw1 = EasyMock.createMock(IOFSwitch.class); - expect(sw1.getId()).andReturn(DatapathId.of(1L)).anyTimes(); - expect(sw1.getOFFactory()).andReturn(factory).anyTimes(); - expect(sw1.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes(); - - sw2 = EasyMock.createMock(IOFSwitch.class); - expect(sw2.getId()).andReturn(DatapathId.of(2L)).anyTimes(); - expect(sw2.getOFFactory()).andReturn(factory).anyTimes(); - expect(sw2.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes(); - - expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes(); - - expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes(); - - // Load the switch map - Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>(); - switches.put(DatapathId.of(1L), sw1); - switches.put(DatapathId.of(2L), sw2); - getMockSwitchService().setSwitches(switches); - - // Build test packet - testPacket = new Ethernet() - .setDestinationMACAddress("00:11:22:33:44:55") - .setSourceMACAddress("00:44:33:22:11:00") - .setEtherType(EthType.IPv4) - .setPayload( - new IPv4() - .setTtl((byte) 128) - .setSourceAddress("192.168.1.1") - .setDestinationAddress("192.168.1.2") - .setPayload(new UDP() - .setSourcePort((short) 5000) - .setDestinationPort((short) 5001) - .setPayload(new Data(new byte[] {0x01})))); - - - - currentDate = new Date(); - - // Mock Packet-in - testPacketSerialized = testPacket.serialize(); - packetIn = factory.buildPacketIn() - .setMatch(factory.buildMatch() - .setExact(MatchField.IN_PORT, OFPort.of(1)) - .setExact(MatchField.ETH_SRC, MacAddress.of("00:44:33:22:11:00")) - .setExact(MatchField.ETH_DST, MacAddress.of("00:11:22:33:44:55")) - .setExact(MatchField.ETH_TYPE, EthType.IPv4) - .setExact(MatchField.IPV4_SRC, IPv4Address.of("192.168.1.1")) - .setExact(MatchField.IPV4_DST, IPv4Address.of("192.168.1.2")) - .setExact(MatchField.IP_PROTO, IpProtocol.UDP) - .setExact(MatchField.UDP_SRC, TransportPort.of(5000)) - .setExact(MatchField.UDP_DST, TransportPort.of(5001)) - .build()) - .setBufferId(OFBufferId.NO_BUFFER) - .setData(testPacketSerialized) - .setReason(OFPacketInReason.NO_MATCH) - .build(); - - // Mock Packet-out - List<OFAction> poactions = new ArrayList<OFAction>(); - poactions.add(factory.actions().output(OFPort.of(3), Integer.MAX_VALUE)); - packetOut = factory.buildPacketOut() - .setBufferId(this.packetIn.getBufferId()) - .setActions(poactions) - .setInPort(OFPort.of(1)) - .setData(testPacketSerialized) - .setXid(15) - .build(); - - // Mock Packet-out with OFPP_FLOOD action - poactions = new ArrayList<OFAction>(); - poactions.add(factory.actions().output(OFPort.FLOOD, Integer.MAX_VALUE)); - packetOutFlooded = factory.buildPacketOut() - .setBufferId(this.packetIn.getBufferId()) - .setInPort(packetIn.getMatch().get(MatchField.IN_PORT)) - .setXid(17) - .setActions(poactions) - .setData(testPacketSerialized) - .build(); - - IFloodlightProviderService.bcStore. - put(cntx, - IFloodlightProviderService.CONTEXT_PI_PAYLOAD, - (Ethernet)testPacket); - } - - enum DestDeviceToLearn { NONE, DEVICE1 ,DEVICE2 }; - public void learnDevices(DestDeviceToLearn destDeviceToLearn) { - // Build src and dest devices - MacAddress dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress(); - MacAddress dataLayerDest = - ((Ethernet)testPacket).getDestinationMACAddress(); - IPv4Address networkSource = - ((IPv4)((Ethernet)testPacket).getPayload()). - getSourceAddress(); - IPv4Address networkDest = - ((IPv4)((Ethernet)testPacket).getPayload()). - getDestinationAddress(); - - reset(topology); - expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))) - .andReturn(true) - .anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))) - .andReturn(true) - .anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))) - .andReturn(true) - .anyTimes(); - replay(topology); - - srcDevice = - deviceManager.learnEntity(dataLayerSource.getLong(), - null, networkSource.getInt(), - 1L, 1); - IDeviceService.fcStore. put(cntx, - IDeviceService.CONTEXT_SRC_DEVICE, - srcDevice); - if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) { - dstDevice1 = - deviceManager.learnEntity(dataLayerDest.getLong(), - null, networkDest.getInt(), - 2L, 3); - IDeviceService.fcStore.put(cntx, - IDeviceService.CONTEXT_DST_DEVICE, - dstDevice1); - } - if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) { - dstDevice2 = - deviceManager.learnEntity(dataLayerDest.getLong(), - null, networkDest.getInt(), - 1L, 3); - IDeviceService.fcStore.put(cntx, - IDeviceService.CONTEXT_DST_DEVICE, - dstDevice2); - } - verify(topology); - } - - @Test - public void testForwardMultiSwitchPath() throws Exception { - learnDevices(DestDeviceToLearn.DEVICE1); - - Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); - Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); - - Route route = new Route(DatapathId.of(1L), DatapathId.of(2L)); - List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); - nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); - nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); - nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(1))); - nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(3))); - route.setPath(nptList); - expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(2L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); - - // Expected Flow-mods - Match match = packetIn.getMatch(); - OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); - List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(action); - - OFFlowMod fm1 = factory.buildFlowAdd() - .setIdleTimeout((short)5) - .setMatch(match) - .setActions(actions) - .setOutPort(action.getPort()) - .setBufferId(OFBufferId.NO_BUFFER) - .setCookie(U64.of(2L << 52)) - .setPriority(1) - .build(); - OFFlowMod fm2 = fm1.createBuilder().build(); - - sw1.write(capture(wc1)); - expectLastCall().anyTimes(); - sw2.write(capture(wc2)); - expectLastCall().anyTimes(); - - reset(topology); - expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); - expect(topology.getL2DomainId(DatapathId.of(2L))).andReturn(DatapathId.of(1L)).anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))).andReturn(true).anyTimes(); - expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); - - // Reset mocks, trigger the packet in, and validate results - replay(sw1, sw2, routingEngine, topology); - forwarding.receive(sw1, this.packetIn, cntx); - verify(sw1, sw2, routingEngine); - - assertTrue(wc1.hasCaptured()); // wc1 should get packetout + flowmod. - assertTrue(wc2.hasCaptured()); // wc2 should be a flowmod. - - List<OFMessage> msglist = wc1.getValues(); - - for (OFMessage m: msglist) { - if (m instanceof OFFlowMod) - assertTrue(OFMessageUtils.equalsIgnoreXid(fm1, m)); - else if (m instanceof OFPacketOut) { - assertTrue(OFMessageUtils.equalsIgnoreXid(packetOut, m)); - } - } - - OFMessage m = wc2.getValue(); - assert (m instanceof OFFlowMod); - assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2)); - } - - @Test - public void testForwardSingleSwitchPath() throws Exception { - learnDevices(DestDeviceToLearn.DEVICE2); - - Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); - Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); - - Route route = new Route(DatapathId.of(1L), DatapathId.of(1L)); - route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); - route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); - expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); - - // Expected Flow-mods - Match match = packetIn.getMatch(); - OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); - List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(action); - - OFFlowMod fm1 = factory.buildFlowAdd() - .setIdleTimeout((short)5) - .setMatch(match) - .setActions(actions) - .setOutPort(OFPort.of(3)) - .setBufferId(OFBufferId.NO_BUFFER) - .setCookie(U64.of(2L<< 52)) - .setPriority(1) - .build(); - - // Record expected packet-outs/flow-mods - sw1.write(capture(wc1)); - expectLastCall().once(); - sw1.write(capture(wc2)); - expectLastCall().once(); - - reset(topology); - expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); - expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes(); - - // Reset mocks, trigger the packet in, and validate results - replay(sw1, sw2, routingEngine, topology); - forwarding.receive(sw1, this.packetIn, cntx); - verify(sw1, sw2, routingEngine); - - assertTrue(wc1.hasCaptured()); - assertTrue(wc2.hasCaptured()); - - assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1)); - assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOut)); - } - - /*TODO OFMessageDamper broken due to XID variability in OFMessages... need to fix @Test */ - public void testFlowModDampening() throws Exception { - learnDevices(DestDeviceToLearn.DEVICE2); - - reset(topology); - expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), OFPort.of(anyShort()))) - .andReturn(true).anyTimes(); - expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); - replay(topology); - - - Route route = new Route(DatapathId.of(1L), DatapathId.of(1L)); - route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); - route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); - expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); - - // Expected Flow-mods - Match match = packetIn.getMatch(); - OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); - List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(action); - - OFFlowMod fm1 = factory.buildFlowAdd() - .setIdleTimeout((short)5) - .setMatch(match) - .setActions(actions) - .setOutPort(OFPort.of(3)) - .setBufferId(OFBufferId.NO_BUFFER) - .setCookie(U64.of(2L << 52)) - .setXid(anyLong()) - .build(); - - // Record expected packet-outs/flow-mods - // We will inject the packet_in 3 times and expect 1 flow mod and - // 3 packet outs due to flow mod dampening - sw1.write(fm1); - expectLastCall().times(1); - // Update new expected XID - sw1.write(packetOut.createBuilder().setXid(anyLong()).build()); - expectLastCall().times(3); - - reset(topology); - expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); - expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes(); - - // Reset mocks, trigger the packet in, and validate results - replay(sw1, routingEngine, topology); - forwarding.receive(sw1, this.packetIn, cntx); - forwarding.receive(sw1, this.packetIn, cntx); - forwarding.receive(sw1, this.packetIn, cntx); - verify(sw1, routingEngine); - } - - @Test - public void testForwardNoPath() throws Exception { - learnDevices(DestDeviceToLearn.NONE); - - // Set no destination attachment point or route - // expect no Flow-mod but expect the packet to be flooded - - Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); - - // Reset mocks, trigger the packet in, and validate results - reset(topology); - expect(topology.isIncomingBroadcastAllowed(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); - expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), - OFPort.of(anyShort()))) - .andReturn(true) - .anyTimes(); - expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) - .andReturn(true).anyTimes(); - // Reset XID to expected (dependent on prior unit tests) - sw1.write(capture(wc1)); - expectLastCall().once(); - replay(sw1, sw2, routingEngine, topology); - forwarding.receive(sw1, this.packetIn, cntx); - verify(sw1, sw2, routingEngine); - - assertTrue(wc1.hasCaptured()); - assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFlooded)); - } - -} + protected FloodlightContext cntx; + protected MockDeviceManager deviceManager; + protected IRoutingService routingEngine; + protected Forwarding forwarding; + protected ITopologyService topology; + protected MockThreadPoolService threadPool; + protected IOFSwitch sw1, sw2; + protected OFFeaturesReply swFeatures; + protected IDevice srcDevice, dstDevice1, dstDevice2; /* reuse for IPv4 and IPv6 */ + protected OFPacketIn packetIn; + protected OFPacketIn packetInIPv6; + protected OFPacketOut packetOut; + protected OFPacketOut packetOutIPv6; + protected OFPacketOut packetOutFlooded; + protected OFPacketOut packetOutFloodedIPv6; + protected IPacket testPacket; + protected IPacket testPacketIPv6; + protected byte[] testPacketSerialized; + protected byte[] testPacketSerializedIPv6; + protected int expected_wildcards; + protected Date currentDate; + private MockSyncService mockSyncService; + private OFFactory factory = OFFactories.getFactory(OFVersion.OF_13); + + + @Override + public void setUp() throws Exception { + super.setUp(); + + cntx = new FloodlightContext(); + + // Module loader setup + mockFloodlightProvider = getMockFloodlightProvider(); + forwarding = new Forwarding(); + threadPool = new MockThreadPoolService(); + deviceManager = new MockDeviceManager(); + routingEngine = createMock(IRoutingService.class); + topology = createMock(ITopologyService.class); + mockSyncService = new MockSyncService(); + DefaultEntityClassifier entityClassifier = new DefaultEntityClassifier(); + + FloodlightModuleContext fmc = new FloodlightModuleContext(); + fmc.addService(IFloodlightProviderService.class, + mockFloodlightProvider); + fmc.addService(IThreadPoolService.class, threadPool); + fmc.addService(ITopologyService.class, topology); + fmc.addService(IRoutingService.class, routingEngine); + fmc.addService(IDeviceService.class, deviceManager); + fmc.addService(IEntityClassifierService.class, entityClassifier); + fmc.addService(ISyncService.class, mockSyncService); + fmc.addService(IDebugCounterService.class, new MockDebugCounterService()); + fmc.addService(IDebugEventService.class, new MockDebugEventService()); + fmc.addService(IOFSwitchService.class, getMockSwitchService()); + + topology.addListener(anyObject(ITopologyListener.class)); + expectLastCall().anyTimes(); + expect(topology.isIncomingBroadcastAllowed(anyObject(DatapathId.class), anyObject(OFPort.class))).andReturn(true).anyTimes(); + replay(topology); + + threadPool.init(fmc); + mockSyncService.init(fmc); + forwarding.init(fmc); + deviceManager.init(fmc); + entityClassifier.init(fmc); + threadPool.startUp(fmc); + mockSyncService.startUp(fmc); + deviceManager.startUp(fmc); + forwarding.startUp(fmc); + entityClassifier.startUp(fmc); + verify(topology); + + swFeatures = factory.buildFeaturesReply().setNBuffers(1000).build(); + // Mock switches + sw1 = EasyMock.createMock(IOFSwitch.class); + expect(sw1.getId()).andReturn(DatapathId.of(1L)).anyTimes(); + expect(sw1.getOFFactory()).andReturn(factory).anyTimes(); + expect(sw1.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes(); + + sw2 = EasyMock.createMock(IOFSwitch.class); + expect(sw2.getId()).andReturn(DatapathId.of(2L)).anyTimes(); + expect(sw2.getOFFactory()).andReturn(factory).anyTimes(); + expect(sw2.getBuffers()).andReturn(swFeatures.getNBuffers()).anyTimes(); + + expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes(); + + expect(sw2.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_TABLE)).andReturn(true).anyTimes(); + + // Load the switch map + Map<DatapathId, IOFSwitch> switches = new HashMap<DatapathId, IOFSwitch>(); + switches.put(DatapathId.of(1L), sw1); + switches.put(DatapathId.of(2L), sw2); + getMockSwitchService().setSwitches(switches); + + // Build test packet + testPacket = new Ethernet() + .setDestinationMACAddress("00:11:22:33:44:55") + .setSourceMACAddress("00:44:33:22:11:00") + .setEtherType(EthType.IPv4) + .setPayload( + new IPv4() + .setTtl((byte) 128) + .setSourceAddress("192.168.1.1") + .setDestinationAddress("192.168.1.2") + .setPayload(new UDP() + .setSourcePort((short) 5000) + .setDestinationPort((short) 5001) + .setPayload(new Data(new byte[] {0x01})))); + + testPacketIPv6 = new Ethernet() + .setDestinationMACAddress("00:11:22:33:44:55") + .setSourceMACAddress("00:44:33:22:11:00") + .setEtherType(EthType.IPv6) + .setPayload( + new IPv6() + .setHopLimit((byte) 128) + .setSourceAddress(IPv6Address.of(1, 1)) + .setDestinationAddress(IPv6Address.of(2, 2)) + .setNextHeader(IpProtocol.UDP) + .setPayload(new UDP() + .setSourcePort((short) 5000) + .setDestinationPort((short) 5001) + .setPayload(new Data(new byte[] {0x01})))); + + currentDate = new Date(); + + // Mock Packet-in + testPacketSerialized = testPacket.serialize(); + testPacketSerializedIPv6 = testPacketIPv6.serialize(); + + packetIn = factory.buildPacketIn() + .setMatch(factory.buildMatch() + .setExact(MatchField.IN_PORT, OFPort.of(1)) + .setExact(MatchField.ETH_SRC, MacAddress.of("00:44:33:22:11:00")) + .setExact(MatchField.ETH_DST, MacAddress.of("00:11:22:33:44:55")) + .setExact(MatchField.ETH_TYPE, EthType.IPv4) + .setExact(MatchField.IPV4_SRC, IPv4Address.of("192.168.1.1")) + .setExact(MatchField.IPV4_DST, IPv4Address.of("192.168.1.2")) + .setExact(MatchField.IP_PROTO, IpProtocol.UDP) + .setExact(MatchField.UDP_SRC, TransportPort.of(5000)) + .setExact(MatchField.UDP_DST, TransportPort.of(5001)) + .build()) + .setBufferId(OFBufferId.NO_BUFFER) + .setData(testPacketSerialized) + .setReason(OFPacketInReason.NO_MATCH) + .build(); + packetInIPv6 = factory.buildPacketIn() + .setMatch(factory.buildMatch() + .setExact(MatchField.IN_PORT, OFPort.of(1)) + .setExact(MatchField.ETH_SRC, MacAddress.of("00:44:33:22:11:00")) + .setExact(MatchField.ETH_DST, MacAddress.of("00:11:22:33:44:55")) + .setExact(MatchField.ETH_TYPE, EthType.IPv6) + .setExact(MatchField.IPV6_SRC, IPv6Address.of(1, 1)) + .setExact(MatchField.IPV6_DST, IPv6Address.of(2, 2)) + .setExact(MatchField.IP_PROTO, IpProtocol.UDP) + .setExact(MatchField.UDP_SRC, TransportPort.of(5000)) + .setExact(MatchField.UDP_DST, TransportPort.of(5001)) + .build()) + .setBufferId(OFBufferId.NO_BUFFER) + .setData(testPacketSerializedIPv6) + .setReason(OFPacketInReason.NO_MATCH) + .build(); + + // Mock Packet-out + List<OFAction> poactions = new ArrayList<OFAction>(); + poactions.add(factory.actions().output(OFPort.of(3), Integer.MAX_VALUE)); + packetOut = factory.buildPacketOut() + .setBufferId(this.packetIn.getBufferId()) + .setActions(poactions) + .setInPort(OFPort.of(1)) + .setData(testPacketSerialized) + .setXid(15) + .build(); + packetOutIPv6 = factory.buildPacketOut() + .setBufferId(this.packetInIPv6.getBufferId()) + .setActions(poactions) + .setInPort(OFPort.of(1)) + .setData(testPacketSerializedIPv6) + .setXid(15) + .build(); + + // Mock Packet-out with OFPP_FLOOD action + poactions = new ArrayList<OFAction>(); + poactions.add(factory.actions().output(OFPort.FLOOD, Integer.MAX_VALUE)); + packetOutFlooded = factory.buildPacketOut() + .setBufferId(this.packetIn.getBufferId()) + .setInPort(packetIn.getMatch().get(MatchField.IN_PORT)) + .setXid(17) + .setActions(poactions) + .setData(testPacketSerialized) + .build(); + packetOutFloodedIPv6 = factory.buildPacketOut() + .setBufferId(this.packetInIPv6.getBufferId()) + .setInPort(packetInIPv6.getMatch().get(MatchField.IN_PORT)) + .setXid(17) + .setActions(poactions) + .setData(testPacketSerializedIPv6) + .build(); + } + + void removeDeviceFromContext() { + IFloodlightProviderService.bcStore. + remove(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + IFloodlightProviderService.bcStore. + remove(cntx, + IDeviceService.CONTEXT_SRC_DEVICE); + IFloodlightProviderService.bcStore. + remove(cntx, + IDeviceService.CONTEXT_DST_DEVICE); + } + + enum DestDeviceToLearn { NONE, DEVICE1 ,DEVICE2 }; + public void learnDevices(DestDeviceToLearn destDeviceToLearn) { + // Build src and dest devices + MacAddress dataLayerSource = ((Ethernet)testPacket).getSourceMACAddress(); + MacAddress dataLayerDest = + ((Ethernet)testPacket).getDestinationMACAddress(); + IPv4Address networkSource = + ((IPv4)((Ethernet)testPacket).getPayload()). + getSourceAddress(); + IPv4Address networkDest = + ((IPv4)((Ethernet)testPacket).getPayload()). + getDestinationAddress(); + + reset(topology); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))) + .andReturn(true) + .anyTimes(); + replay(topology); + + srcDevice = + deviceManager.learnEntity(dataLayerSource, VlanVid.ZERO, + networkSource, IPv6Address.NONE, + DatapathId.of(1), OFPort.of(1)); + IDeviceService.fcStore. put(cntx, + IDeviceService.CONTEXT_SRC_DEVICE, + srcDevice); + if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) { + dstDevice1 = + deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, + networkDest, IPv6Address.NONE, + DatapathId.of(2), OFPort.of(3)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice1); + } + if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) { + dstDevice2 = + deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, + networkDest, IPv6Address.NONE, + DatapathId.of(1), OFPort.of(3)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice2); + } + verify(topology); + + IFloodlightProviderService.bcStore. + put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + (Ethernet)testPacket); + } + + public void learnDevicesIPv6(DestDeviceToLearn destDeviceToLearn) { + // Build src and dest devices + MacAddress dataLayerSource = ((Ethernet)testPacketIPv6).getSourceMACAddress(); + MacAddress dataLayerDest = + ((Ethernet)testPacketIPv6).getDestinationMACAddress(); + IPv6Address networkSource = + ((IPv6)((Ethernet)testPacketIPv6).getPayload()). + getSourceAddress(); + IPv6Address networkDest = + ((IPv6)((Ethernet)testPacketIPv6).getPayload()). + getDestinationAddress(); + + reset(topology); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))) + .andReturn(true) + .anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))) + .andReturn(true) + .anyTimes(); + replay(topology); + + srcDevice = + deviceManager.learnEntity(dataLayerSource, VlanVid.ZERO, + IPv4Address.NONE, networkSource, + DatapathId.of(1), OFPort.of(1)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_SRC_DEVICE, + srcDevice); + if (destDeviceToLearn == DestDeviceToLearn.DEVICE1) { + dstDevice1 = + deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, + IPv4Address.NONE, networkDest, + DatapathId.of(2), OFPort.of(3)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice1); + } + if (destDeviceToLearn == DestDeviceToLearn.DEVICE2) { + dstDevice2 = + deviceManager.learnEntity(dataLayerDest, VlanVid.ZERO, + IPv4Address.NONE, networkDest, + DatapathId.of(1), OFPort.of(3)); + IDeviceService.fcStore.put(cntx, + IDeviceService.CONTEXT_DST_DEVICE, + dstDevice2); + } + verify(topology); + + IFloodlightProviderService.bcStore. + put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + (Ethernet)testPacketIPv6); + } + + @Test + public void testForwardMultiSwitchPath() throws Exception { + learnDevices(DestDeviceToLearn.DEVICE1); + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); + + Route route = new Route(DatapathId.of(1L), DatapathId.of(2L)); + List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); + nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); + nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); + nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(1))); + nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(3))); + route.setPath(nptList); + expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(2L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + Match match = packetIn.getMatch(); + OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = factory.buildFlowAdd() + .setIdleTimeout((short)5) + .setMatch(match) + .setActions(actions) + .setOutPort(action.getPort()) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.of(2L << 52)) + .setPriority(1) + .build(); + OFFlowMod fm2 = fm1.createBuilder().build(); + + sw1.write(capture(wc1)); + expectLastCall().anyTimes(); + sw2.write(capture(wc2)); + expectLastCall().anyTimes(); + + reset(topology); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(2L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))).andReturn(true).anyTimes(); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetIn, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); // wc1 should get packetout + flowmod. + assertTrue(wc2.hasCaptured()); // wc2 should be a flowmod. + + List<OFMessage> msglist = wc1.getValues(); + + for (OFMessage m: msglist) { + if (m instanceof OFFlowMod) + assertTrue(OFMessageUtils.equalsIgnoreXid(fm1, m)); + else if (m instanceof OFPacketOut) { + assertTrue(OFMessageUtils.equalsIgnoreXid(packetOut, m)); + } + } + + OFMessage m = wc2.getValue(); + assert (m instanceof OFFlowMod); + assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2)); + + removeDeviceFromContext(); + } + + @Test + public void testForwardMultiSwitchPathIPv6() throws Exception { + learnDevicesIPv6(DestDeviceToLearn.DEVICE1); + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); + + Route route = new Route(DatapathId.of(1L), DatapathId.of(2L)); + List<NodePortTuple> nptList = new ArrayList<NodePortTuple>(); + nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); + nptList.add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); + nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(1))); + nptList.add(new NodePortTuple(DatapathId.of(2L), OFPort.of(3))); + route.setPath(nptList); + expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(2L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + Match match = packetInIPv6.getMatch(); + OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = factory.buildFlowAdd() + .setIdleTimeout((short)5) + .setMatch(match) + .setActions(actions) + .setOutPort(action.getPort()) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.of(2L << 52)) + .setPriority(1) + .build(); + OFFlowMod fm2 = fm1.createBuilder().build(); + + sw1.write(capture(wc1)); + expectLastCall().anyTimes(); + sw2.write(capture(wc2)); + expectLastCall().anyTimes(); + + reset(topology); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(2L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(2L), OFPort.of(3))).andReturn(true).anyTimes(); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetInIPv6, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); // wc1 should get packetout + flowmod. + assertTrue(wc2.hasCaptured()); // wc2 should be a flowmod. + + List<OFMessage> msglist = wc1.getValues(); + + for (OFMessage m: msglist) { + if (m instanceof OFFlowMod) + assertTrue(OFMessageUtils.equalsIgnoreXid(fm1, m)); + else if (m instanceof OFPacketOut) { + assertTrue(OFMessageUtils.equalsIgnoreXid(packetOutIPv6, m)); + } + } + + OFMessage m = wc2.getValue(); + assert (m instanceof OFFlowMod); + assertTrue(OFMessageUtils.equalsIgnoreXid(m, fm2)); + + removeDeviceFromContext(); + } + + @Test + public void testForwardSingleSwitchPath() throws Exception { + learnDevices(DestDeviceToLearn.DEVICE2); + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); + + Route route = new Route(DatapathId.of(1L), DatapathId.of(1L)); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); + expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + Match match = packetIn.getMatch(); + OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = factory.buildFlowAdd() + .setIdleTimeout((short)5) + .setMatch(match) + .setActions(actions) + .setOutPort(OFPort.of(3)) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.of(2L<< 52)) + .setPriority(1) + .build(); + + // Record expected packet-outs/flow-mods + sw1.write(capture(wc1)); + expectLastCall().once(); + sw1.write(capture(wc2)); + expectLastCall().once(); + + reset(topology); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetIn, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); + assertTrue(wc2.hasCaptured()); + + assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1)); + assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOut)); + + removeDeviceFromContext(); + } + + @Test + public void testForwardSingleSwitchPathIPv6() throws Exception { + learnDevicesIPv6(DestDeviceToLearn.DEVICE2); + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + Capture<OFMessage> wc2 = new Capture<OFMessage>(CaptureType.ALL); + + Route route = new Route(DatapathId.of(1L), DatapathId.of(1L)); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); + expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + Match match = packetInIPv6.getMatch(); + OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = factory.buildFlowAdd() + .setIdleTimeout((short)5) + .setMatch(match) + .setActions(actions) + .setOutPort(OFPort.of(3)) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.of(2L<< 52)) + .setPriority(1) + .build(); + + // Record expected packet-outs/flow-mods + sw1.write(capture(wc1)); + expectLastCall().once(); + sw1.write(capture(wc2)); + expectLastCall().once(); + + reset(topology); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetInIPv6, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); + assertTrue(wc2.hasCaptured()); + + assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), fm1)); + assertTrue(OFMessageUtils.equalsIgnoreXid(wc2.getValue(), packetOutIPv6)); + + removeDeviceFromContext(); + } + + /*TODO OFMessageDamper broken due to XID variability in OFMessages... need to fix @Test */ + /*TODO make an IPv6 test for this once OFMessageDamper fixed */ + public void testFlowModDampening() throws Exception { + learnDevices(DestDeviceToLearn.DEVICE2); + + reset(topology); + expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), OFPort.of(anyShort()))) + .andReturn(true).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + replay(topology); + + + Route route = new Route(DatapathId.of(1L), DatapathId.of(1L)); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(1))); + route.getPath().add(new NodePortTuple(DatapathId.of(1L), OFPort.of(3))); + expect(routingEngine.getRoute(DatapathId.of(1L), OFPort.of(1), DatapathId.of(1L), OFPort.of(3), U64.ZERO)).andReturn(route).atLeastOnce(); + + // Expected Flow-mods + Match match = packetIn.getMatch(); + OFActionOutput action = factory.actions().output(OFPort.of(3), Integer.MAX_VALUE); + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(action); + + OFFlowMod fm1 = factory.buildFlowAdd() + .setIdleTimeout((short)5) + .setMatch(match) + .setActions(actions) + .setOutPort(OFPort.of(3)) + .setBufferId(OFBufferId.NO_BUFFER) + .setCookie(U64.of(2L << 52)) + .setXid(anyLong()) + .build(); + + // Record expected packet-outs/flow-mods + // We will inject the packet_in 3 times and expect 1 flow mod and + // 3 packet outs due to flow mod dampening + sw1.write(fm1); + expectLastCall().times(1); + // Update new expected XID + sw1.write(packetOut.createBuilder().setXid(anyLong()).build()); + expectLastCall().times(3); + + reset(topology); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(anyLong()), OFPort.of(anyShort()))).andReturn(true).anyTimes(); + expect(topology.getL2DomainId(DatapathId.of(1L))).andReturn(DatapathId.of(1L)).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(1L), OFPort.of(3))).andReturn(true).anyTimes(); + + // Reset mocks, trigger the packet in, and validate results + replay(sw1, routingEngine, topology); + forwarding.receive(sw1, this.packetIn, cntx); + forwarding.receive(sw1, this.packetIn, cntx); + forwarding.receive(sw1, this.packetIn, cntx); + verify(sw1, routingEngine); + + removeDeviceFromContext(); + } + + @Test + public void testForwardNoPath() throws Exception { + learnDevices(DestDeviceToLearn.NONE); + + // Set no destination attachment point or route + // expect no Flow-mod but expect the packet to be flooded + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + + // Reset mocks, trigger the packet in, and validate results + reset(topology); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), + OFPort.of(anyShort()))) + .andReturn(true) + .anyTimes(); + expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) + .andReturn(true).anyTimes(); + // Reset XID to expected (dependent on prior unit tests) + sw1.write(capture(wc1)); + expectLastCall().once(); + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetIn, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); + assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFlooded)); + + removeDeviceFromContext(); + } + + @Test + public void testForwardNoPathIPv6() throws Exception { + learnDevicesIPv6(DestDeviceToLearn.NONE); + + // Set no destination attachment point or route + // expect no Flow-mod but expect the packet to be flooded + + Capture<OFMessage> wc1 = new Capture<OFMessage>(CaptureType.ALL); + + // Reset mocks, trigger the packet in, and validate results + reset(topology); + expect(topology.isIncomingBroadcastAllowed(DatapathId.of(1L), OFPort.of(1))).andReturn(true).anyTimes(); + expect(topology.isAttachmentPointPort(DatapathId.of(anyLong()), + OFPort.of(anyShort()))) + .andReturn(true) + .anyTimes(); + expect(sw1.hasAttribute(IOFSwitch.PROP_SUPPORTS_OFPP_FLOOD)) + .andReturn(true).anyTimes(); + // Reset XID to expected (dependent on prior unit tests) + sw1.write(capture(wc1)); + expectLastCall().once(); + replay(sw1, sw2, routingEngine, topology); + forwarding.receive(sw1, this.packetInIPv6, cntx); + verify(sw1, sw2, routingEngine); + + assertTrue(wc1.hasCaptured()); + assertTrue(OFMessageUtils.equalsIgnoreXid(wc1.getValue(), packetOutFloodedIPv6)); + + removeDeviceFromContext(); + } +} \ No newline at end of file diff --git a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java index 231db1828741acd313defd362be9559f575f8df0..0aa8d933ea11f233eaad23cd3d94847503d64ec3 100644 --- a/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java +++ b/src/test/java/net/floodlightcontroller/loadbalancer/LoadBalancerTest.java @@ -47,11 +47,13 @@ import org.projectfloodlight.openflow.protocol.OFPacketOut; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.IpProtocol; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFPort; import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.VlanVid; import org.projectfloodlight.openflow.protocol.OFPacketInReason; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.protocol.match.MatchField; @@ -490,10 +492,10 @@ public class LoadBalancerTest extends FloodlightTestCase { .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REQUEST) - .setSenderHardwareAddress(HexString.fromHexString("00:00:00:00:00:01")) - .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1")) - .setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:00")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100"))); + .setSenderHardwareAddress(MacAddress.of("00:00:00:00:00:01")) + .setSenderProtocolAddress(IPv4Address.of("10.0.0.1")) + .setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:00")) + .setTargetProtocolAddress(IPv4Address.of("10.0.0.100"))); arpRequest1Serialized = arpRequest1.serialize(); @@ -522,10 +524,10 @@ public class LoadBalancerTest extends FloodlightTestCase { .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) - .setSenderHardwareAddress(HexString.fromHexString(LBVip.LB_PROXY_MAC)) - .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.100")) - .setTargetHardwareAddress(HexString.fromHexString("00:00:00:00:00:01")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("10.0.0.1"))); + .setSenderHardwareAddress(MacAddress.of(LBVip.LB_PROXY_MAC)) + .setSenderProtocolAddress(IPv4Address.of("10.0.0.100")) + .setTargetHardwareAddress(MacAddress.of("00:00:00:00:00:01")) + .setTargetProtocolAddress(IPv4Address.of("10.0.0.1"))); arpReply1Serialized = arpReply1.serialize(); @@ -621,18 +623,18 @@ public class LoadBalancerTest extends FloodlightTestCase { MacAddress dataLayerDest2 = MacAddress.of("00:00:00:00:00:04"); IPv4Address networkDest2 = IPv4Address.of("10.0.0.4"); - deviceManager.learnEntity(dataLayerSource1.getLong(), - null, networkSource1.getInt(), - 1L, 1); - deviceManager.learnEntity(dataLayerSource2.getLong(), - null, networkSource2.getInt(), - 1L, 2); - deviceManager.learnEntity(dataLayerDest1.getLong(), - null, networkDest1.getInt(), - 1L, 3); - deviceManager.learnEntity(dataLayerDest2.getLong(), - null, networkDest2.getInt(), - 1L, 4); + deviceManager.learnEntity(dataLayerSource1, + VlanVid.ZERO, networkSource1, IPv6Address.NONE, + DatapathId.of(1), OFPort.of(1)); + deviceManager.learnEntity(dataLayerSource2, + VlanVid.ZERO, networkSource2, IPv6Address.NONE, + DatapathId.of(1), OFPort.of(2)); + deviceManager.learnEntity(dataLayerDest1, + VlanVid.ZERO, networkDest1, IPv6Address.NONE, + DatapathId.of(1), OFPort.of(3)); + deviceManager.learnEntity(dataLayerDest2, + VlanVid.ZERO, networkDest2, IPv6Address.NONE, + DatapathId.of(1), OFPort.of(4)); // in bound #1 Route route1 = new Route(DatapathId.of(1L), DatapathId.of(1L)); diff --git a/src/test/java/net/floodlightcontroller/packet/IPv6Test.java b/src/test/java/net/floodlightcontroller/packet/IPv6Test.java new file mode 100644 index 0000000000000000000000000000000000000000..78607cd836de138a23de516745f2b63aea88028b --- /dev/null +++ b/src/test/java/net/floodlightcontroller/packet/IPv6Test.java @@ -0,0 +1,58 @@ +package net.floodlightcontroller.packet; + +import static org.junit.Assert.assertTrue; +import java.util.Arrays; +import org.junit.Test; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.IpProtocol; + +/** + * @author Jacob Chappell (jacob.chappell@uky.edu) + */ +public class IPv6Test { + @Test + public void testSerializeWithoutPayload() { + byte[] expected = new byte[] { + 0x64, 0x2B, 0x16, (byte) 0x95, 0x00, 0x00, + 0x11, (byte) 0xE1, (byte) 0xFE, (byte) 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7A, (byte) 0xC5, (byte) 0xFF, (byte) 0xFE, + 0x2E, 0x77, 0x35, (byte) 0xFE, (byte) 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x5D, (byte) 0xFF, (byte) 0xFE, + (byte) 0xC2, 0x30, (byte) 0xFD + }; + IPv6 packet = (new IPv6()) + .setTrafficClass((byte) 0x42) + .setFlowLabel(0xB1695) + .setPayloadLength((short) 0) + .setNextHeader(IpProtocol.of((short) 0x11)) + .setHopLimit((byte) 0xE1) + .setSourceAddress(IPv6Address.of("fe80::7a:c5ff:fe2e:7735")) + .setDestinationAddress(IPv6Address.of("fe80::77:5dff:fec2:30fd")); + byte[] actual = packet.serialize(); + assertTrue(Arrays.equals(expected, actual)); + } + + @Test + public void testDeserialize() throws PacketParsingException { + byte[] spudPacket = { + 0x64, 0x2B, 0x16, (byte) 0x95, 0x00, 0x15, + 0x11, (byte) 0xE1, (byte) 0xFE, (byte) 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7A, (byte) 0xC5, (byte) 0xFF, (byte) 0xFE, + 0x2E, 0x77, 0x35, (byte) 0xFE, (byte) 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x5D, (byte) 0xFF, (byte) 0xFE, + (byte) 0xC2, 0x30, (byte) 0xFD, (byte) 0xD2, + 0x01, 0x05, 0x7A, 0x00, 0x15, (byte) 0xF6, + (byte) 0xC8, (byte) 0xD8, 0x00, 0x00, + (byte) 0xD8, 0x4A, (byte) 0xC3, (byte) 0xF2, + 0x02, 0x44, 0x75, (byte) 0x97, 0x69, 0x40 + }; + IPv6 packet = new IPv6(); + packet.deserialize(spudPacket, 0, spudPacket.length); + byte[] packetSerialized = packet.serialize(); + assertTrue(Arrays.equals(spudPacket, packetSerialized)); + } +} diff --git a/src/test/java/net/floodlightcontroller/packet/PacketTest.java b/src/test/java/net/floodlightcontroller/packet/PacketTest.java index f62f1a8cbc1a1a3d02426dab629b842d490e0368..65fafb9dd8ed8afadd33171743edc8b825db5d29 100644 --- a/src/test/java/net/floodlightcontroller/packet/PacketTest.java +++ b/src/test/java/net/floodlightcontroller/packet/PacketTest.java @@ -18,11 +18,11 @@ package net.floodlightcontroller.packet; import static org.junit.Assert.*; -import java.util.Arrays; - import org.junit.Before; import org.junit.Test; import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.MacAddress; public class PacketTest { protected IPacket pkt1, pkt2, pkt3, pkt4; @@ -57,10 +57,10 @@ public class PacketTest { .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) - .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01")) - .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); + .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01")) + .setSenderProtocolAddress(IPv4Address.of("192.168.1.1")) + .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55")) + .setTargetProtocolAddress(IPv4Address.of("192.168.1.2"))); this.pkt3 = new Ethernet() @@ -74,10 +74,10 @@ public class PacketTest { .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) - .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01")) - .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); + .setSenderHardwareAddress(MacAddress.of("00:44:33:22:11:01")) + .setSenderProtocolAddress(IPv4Address.of("192.168.1.1")) + .setTargetHardwareAddress(MacAddress.of("00:11:22:33:44:55")) + .setTargetProtocolAddress(IPv4Address.of("192.168.1.2"))); this.pkt4 = new Ethernet() .setDestinationMACAddress("FF:FF:FF:FF:FF:FF") @@ -124,9 +124,8 @@ public class PacketTest { if (pkt instanceof ARP) { ARP arp = (ARP)pkt; ARP newArp = (ARP)newPkt; - newArp.setSenderProtocolAddress(new byte[] {1,2,3,4}); - assertEquals(false, Arrays.equals(newArp.getSenderProtocolAddress(), - arp.getSenderProtocolAddress())); + newArp.setSenderProtocolAddress(IPv4Address.of(new byte[] {1,2,3,4})); + assertEquals(false, newArp.getSenderProtocolAddress().equals(arp.getSenderProtocolAddress())); assertEquals(false, newPkt.equals(pkt)); } else { byte[] dummyData = dummyPkt.serialize().clone(); diff --git a/src/test/java/net/floodlightcontroller/packet/SPUDTest.java b/src/test/java/net/floodlightcontroller/packet/SPUDTest.java new file mode 100644 index 0000000000000000000000000000000000000000..82cd29e2d6e30e8d632d97869de388882e403042 --- /dev/null +++ b/src/test/java/net/floodlightcontroller/packet/SPUDTest.java @@ -0,0 +1,114 @@ +package net.floodlightcontroller.packet; + +import static org.junit.Assert.assertTrue; +import java.util.Arrays; +import org.junit.Test; + +/** + * @author Jacob Chappell (jacob.chappell@uky.edu) + */ +public class SPUDTest { + @Test + public void testSerializeCommandOpen() { + byte[] expected = new byte[] { + (byte) 0xd8, 0x00, 0x00, (byte) 0xd8, + (byte) 0xb6, 0x40, 0x17, (byte) 0x88, + 0x0a, 0x51, 0x01, 0x07, 0x40 + }; + SPUD packet = (new SPUD()) + .setTubeID(0xb64017880a510107L) + .setCommand(SPUD.COMMAND_OPEN) + .setADEC(false) + .setPDEC(false) + .setReserved((byte) 0); + byte[] actual = packet.serialize(); + assertTrue(Arrays.equals(expected, actual)); + } + + @Test + public void testSerializeCommandDataEmpty() { + byte[] expected = new byte[] { + (byte) 0xd8, 0x00, 0x00, (byte) 0xd8, + (byte) 0xb6, 0x40, 0x17, (byte) 0x88, + 0x0a, 0x51, 0x01, 0x07, 0x00 + }; + SPUD packet = (new SPUD()) + .setTubeID(0xb64017880a510107L) + .setCommand(SPUD.COMMAND_DATA) + .setADEC(false) + .setPDEC(false) + .setReserved((byte) 0); + byte[] actual = packet.serialize(); + assertTrue(Arrays.equals(expected, actual)); + } + + @Test + public void testSerializeCommandDataEmptyWithADEC() { + byte[] expected = new byte[] { + (byte) 0xd8, 0x00, 0x00, (byte) 0xd8, + (byte) 0xb6, 0x40, 0x17, (byte) 0x88, + 0x0a, 0x51, 0x01, 0x07, 0x20 + }; + SPUD packet = (new SPUD()) + .setTubeID(0xb64017880a510107L) + .setCommand(SPUD.COMMAND_DATA) + .setADEC(true) + .setPDEC(false) + .setReserved((byte) 0); + byte[] actual = packet.serialize(); + assertTrue(Arrays.equals(expected, actual)); + } + + @Test + public void testSerializeCommandDataEmptyWithPDEC() { + byte[] expected = new byte[] { + (byte) 0xd8, 0x00, 0x00, (byte) 0xd8, + (byte) 0xb6, 0x40, 0x17, (byte) 0x88, + 0x0a, 0x51, 0x01, 0x07, 0x10 + }; + SPUD packet = (new SPUD()) + .setTubeID(0xb64017880a510107L) + .setCommand(SPUD.COMMAND_DATA) + .setADEC(false) + .setPDEC(true) + .setReserved((byte) 0); + byte[] actual = packet.serialize(); + assertTrue(Arrays.equals(expected, actual)); + } + + @Test + public void testSerializeCommandDataEmptyWithBoth() { + byte[] expected = new byte[] { + (byte) 0xd8, 0x00, 0x00, (byte) 0xd8, + (byte) 0xb6, 0x40, 0x17, (byte) 0x88, + 0x0a, 0x51, 0x01, 0x07, 0x30 + }; + SPUD packet = (new SPUD()) + .setTubeID(0xb64017880a510107L) + .setCommand(SPUD.COMMAND_DATA) + .setADEC(true) + .setPDEC(true) + .setReserved((byte) 0); + byte[] actual = packet.serialize(); + assertTrue(Arrays.equals(expected, actual)); + } + + @Test + public void testDeserialize() throws PacketParsingException { + byte[] spudPacket = { + (byte) 0xd8, 0x00, 0x00, (byte) 0xd8, (byte) 0xb6, + 0x40, 0x17, (byte) 0x88, 0x0a, 0x51, 0x01, 0x07, + 0x00, (byte) 0xa1, 0x00, (byte) 0xa2, 0x68, 0x75, + 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x65, + 0x4a, 0x61, 0x63, 0x6f, 0x62, 0x67, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x68, 0x61, + 0x73, 0x20, 0x6a, 0x6f, 0x69, 0x6e, 0x65, 0x64, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x6f, 0x6f, + 0x6d + }; + SPUD packet = new SPUD(); + packet.deserialize(spudPacket, 0, spudPacket.length); + byte[] packetSerialized = packet.serialize(); + assertTrue(Arrays.equals(spudPacket, packetSerialized)); + } +} diff --git a/src/test/java/net/floodlightcontroller/packet/UDPTest.java b/src/test/java/net/floodlightcontroller/packet/UDPTest.java index bed42a2e86dea1344e2963b2d56c1cf6585f63f1..3224c2747f0f33a13132d2cdd5b52c18f35eaa69 100644 --- a/src/test/java/net/floodlightcontroller/packet/UDPTest.java +++ b/src/test/java/net/floodlightcontroller/packet/UDPTest.java @@ -1,7 +1,7 @@ /** -* Copyright 2011, Big Switch Networks, Inc. +* Copyright 2011, Big Switch Networks, Inc. * Originally created by David Erickson, Stanford University -* +* * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at @@ -16,10 +16,11 @@ **/ /** - * + * */ package net.floodlightcontroller.packet; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.Arrays; @@ -52,4 +53,40 @@ public class UDPTest { byte[] actual = packet.serialize(); assertTrue(Arrays.equals(expected, actual)); } + + @Test + public void testDeserializeNotSPUD() throws PacketParsingException { + byte[] data = new byte[] { + 0x04, (byte) 0x89, 0x00, 0x35, 0x00, 0x2C, + (byte) 0xAB, (byte) 0xB4, 0x00, 0x01, 0x01, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x70, 0x6F, 0x70, 0x64, + 0x02, 0x69, 0x78, 0x06, 0x6E, 0x65, 0x74, + 0x63, 0x6F, 0x6D, 0x03, 0x63, 0x6F, 0x6D, + 0x00, 0x00, 0x01, 0x00, 0x01 + }; + UDP packet = new UDP(); + packet.deserialize(data, 0, data.length); + IPacket thePayload = packet.getPayload(); + assertFalse(thePayload instanceof SPUD); + byte[] packetSerialized = packet.serialize(); + assertTrue(Arrays.equals(data, packetSerialized)); + } + + @Test + public void testDeserializeSPUD() throws PacketParsingException { + byte[] data = new byte[] { + (byte) 0xd5, (byte) 0xdf, (byte) 0x98, 0x27, + 0x00, 0x15, (byte) 0xfe, 0x28, (byte) 0xd8, + 0x00, 0x00, (byte) 0xd8, (byte) 0xc2, 0x6f, + 0x7a, 0x7d, 0x56, (byte) 0xa2, (byte) 0xe5, + (byte) 0xa8, 0x40 + }; + UDP packet = new UDP(); + packet.deserialize(data, 0, data.length); + IPacket thePayload = packet.getPayload(); + assertTrue(thePayload instanceof SPUD); + byte[] packetSerialized = packet.serialize(); + assertTrue(Arrays.equals(data, packetSerialized)); + } } diff --git a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java index c11a14372eb33ad7d007e6d75c05bb2d1d68cc73..1416e8353b59fb7b82bf42a5ad95a8d37c2eb10e 100644 --- a/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java +++ b/src/test/java/net/floodlightcontroller/util/OFMessageDamperMockSwitch.java @@ -183,10 +183,16 @@ public class OFMessageDamperMockSwitch implements IOFSwitch { } @Override - public short getTables() { + public short getNumTables() { fail("Unexpected method call"); return 0; } + + @Override + public Collection<TableId> getTables() { + fail("Unexpected method call"); + return null; + } @Override public boolean attributeEquals(String name, Object other) { diff --git a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java index 9a90fef2d53ecbe616e64c8f94f172c3998ebd56..3c07e937b3a309833f9e283b69a24441b952522a 100644 --- a/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java +++ b/src/test/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilterTest.java @@ -32,9 +32,11 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.DatapathId; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; import org.projectfloodlight.openflow.types.MacAddress; import org.projectfloodlight.openflow.types.OFBufferId; import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.VlanVid; import org.sdnplatform.sync.ISyncService; import org.sdnplatform.sync.test.MockSyncService; @@ -357,8 +359,8 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase { IFloodlightProviderService.bcStore.put(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD, (Ethernet)mac1ToGwPacketIntestPacket); - deviceService.learnEntity(((Ethernet)mac1ToGwPacketIntestPacket).getDestinationMACAddress().getLong(), - null, IPv4.toIPv4Address(gw1), null, null); + deviceService.learnEntity(((Ethernet)mac1ToGwPacketIntestPacket).getDestinationMACAddress(), + VlanVid.ZERO, IPv4Address.of(gw1), IPv6Address.NONE, DatapathId.NONE, OFPort.ZERO); Command ret = listener.receive(sw1, mac1ToGwPacketIn, cntx); assertTrue(ret == Command.CONTINUE); }