diff --git a/.gitignore b/.gitignore index dcc106a8e1436ff767ad147545bdfe308800b665..6bb47c581906a2d592173e1b0532486f4ace9bdc 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ thrift *.swp *.pyc findbugs-results +*.launch diff --git a/build.xml b/build.xml index 79d64b1f1c29c68f6cfd490e8e6a9942f1463856..0a3eb4bcdf095b74b5a1eb32ba8d19bbc63b33c9 100644 --- a/build.xml +++ b/build.xml @@ -283,10 +283,12 @@ <target name="findbugs-xml" depends="init,compile"> <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" classpath="${findbugs.home}/lib/findbugs-ant.jar"/> <mkdir dir="${findbugs.results}"/> - <findbugs home="${findbugs.home}" + <findbugs + home="${findbugs.home}" output="xml" excludeFilter="${basedir}/findbugs-exclude.xml" - outputFile="${findbugs.results}/results.xml" reportLevel='high'> + jvmargs="-Xmx1024m" + outputFile="${findbugs.results}/results.xml"> <sourcePath path="${source}" /> <sourcePath path="${thrift.out.dir}" /> <class location="${build}" /> @@ -299,9 +301,11 @@ <target name="findbugs" depends="init,compile"> <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask" classpath="${findbugs.home}/lib/findbugs-ant.jar"/> <mkdir dir="${findbugs.results}"/> - <findbugs home="${findbugs.home}" + <findbugs + home="${findbugs.home}" output="html" excludeFilter="${basedir}/findbugs-exclude.xml" + jvmargs="-Xmx1024m" outputFile="${findbugs.results}/results.html" > <sourcePath path="${source}" /> <sourcePath path="${thrift.out.dir}" /> diff --git a/findbugs-exclude.xml b/findbugs-exclude.xml index bb2e5f327c1d1c56d911e9c3f38351b233727cce..9baffdda0e4c7805c197c34025aae9274b3a0075 100644 --- a/findbugs-exclude.xml +++ b/findbugs-exclude.xml @@ -1,10 +1,20 @@ <FindBugsFilter> <!--- Checks disabled on generated code --> + <Match> <!-- don't complain about switch statements / external representation exposed by generated parser files --> <Class name="~net\.bigdb\.(query|yang)\.parser\..*(Lexer|Parser)" /> - <Bug pattern="SF_SWITCH_NO_DEFAULT,EI_EXPOSE_REP" /> + <Or> + <Bug pattern="SF_SWITCH_NO_DEFAULT,EI_EXPOSE_REP,SBSC_USE_STRINGBUFFER_CONCATENATION" /> + <Bug category="PERFORMANCE,MALICIOUS_CODE" /> + </Or> + </Match> + + <Match> + <!-- Exclude warnings in generated thrift class files --> + <Class name="~net\.floodlightcontroller\.packetstreamer\.thrift\..*" /> + <Bug pattern="CN_IDIOM,DLS_DEAD_LOCAL_STORE" /> </Match> <!-- checks disabled because of too many false positives --> diff --git a/setup-eclipse.sh b/setup-eclipse.sh index 01a6a95ef0f34cee64e85d0707c3d5b29ca0d219..4224a3c5c982f4fee5768a0e5bd788ea36f0b765 100755 --- a/setup-eclipse.sh +++ b/setup-eclipse.sh @@ -25,6 +25,36 @@ cat >"$d/.project" <<EOF </projectDescription> EOF +cat >"$d/Floodlight-Default-Conf.launch" << EOF +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> + <listEntry value="/floodlight/src/main/java/net/floodlightcontroller/core/Main.java"/> + </listAttribute> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> + <listEntry value="1"/> + </listAttribute> + <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="net.floodlightcontroller.core.Main"/> + <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="floodlight"/> + <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/> +</launchConfiguration> +EOF + +cat > "$d/Floodlight-Quantum-Conf.launch" << EOF +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> + <listEntry value="/floodlight/src/main/java/net/floodlightcontroller/core/Main.java"/> + </listAttribute> + <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> + <listEntry value="1"/> + </listAttribute> + <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="net.floodlightcontroller.core.Main"/> + <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-cf src/main/resources/quantum.properties"/> + <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="floodlight"/> + <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/> +</launchConfiguration> +EOF cat >"$d/.classpath" <<EOF <?xml version="1.0" encoding="UTF-8"?> diff --git a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java index e73972ac6fe5f857e7de69fec56b47b79851cdb5..683e0dee8436e941e5663378d3e352f38cafafc9 100644 --- a/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java +++ b/src/main/java/net/floodlightcontroller/core/IFloodlightProviderService.java @@ -88,6 +88,11 @@ public interface IFloodlightProviderService extends */ public Role getRole(); + /** + * Get the current role of the controller + */ + public RoleInfo getRoleInfo(); + /** * Get the current mapping of controller IDs to their IP addresses * Returns a copy of the current mapping. @@ -95,15 +100,13 @@ public interface IFloodlightProviderService extends */ public Map<String,String> getControllerNodeIPs(); - /** - * Gets the ID of the controller - */ - public String getControllerId(); /** * Set the role of the controller + * @param role The new role for the controller node + * @param changeDescription The reason or other information for this role change */ - public void setRole(Role role); + public void setRole(Role role, String changeDescription); /** * Add a switch listener @@ -208,6 +211,17 @@ public interface IFloodlightProviderService extends */ public void setAlwaysClearFlowsOnSwAdd(boolean value); + /** + * Get controller memory information + */ + public Map<String, Long> getMemory(); + + /** + * returns the uptime of this controller. + * @return + */ + public Long getUptime(); + /** * Adds an OFSwitch driver * @param desc The starting portion of switch's manufacturer string diff --git a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java index 98eb372404b3bbcf490d5dcde5bd43baaee43afc..8fa298ddc3bd96fb260373b0757291725f6833ff 100644 --- a/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java +++ b/src/main/java/net/floodlightcontroller/core/OFSwitchBase.java @@ -48,6 +48,7 @@ import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.codehaus.jackson.map.ser.ToStringSerializer; import org.jboss.netty.channel.Channel; +import org.openflow.protocol.OFBarrierRequest; import org.openflow.protocol.OFFeaturesReply; import org.openflow.protocol.OFFlowMod; import org.openflow.protocol.OFMatch; @@ -509,10 +510,18 @@ public abstract class OFSwitchBase implements IOFSwitch { .setCommand(OFFlowMod.OFPFC_DELETE) .setOutPort(OFPort.OFPP_NONE) .setLength(U16.t(OFFlowMod.MINIMUM_LENGTH)); + fm.setXid(getNextTransactionId()); + OFMessage barrierMsg = (OFBarrierRequest) + floodlightProvider.getOFMessageFactory().getMessage( + OFType.BARRIER_REQUEST); + barrierMsg.setXid(getNextTransactionId()); try { List<OFMessage> msglist = new ArrayList<OFMessage>(1); msglist.add(fm); channel.write(msglist); + msglist = new ArrayList<OFMessage>(1); + msglist.add(barrierMsg); + channel.write(msglist); } catch (Exception e) { log.error("Failed to clear all flows on switch " + this, e); } diff --git a/src/main/java/net/floodlightcontroller/core/RoleInfo.java b/src/main/java/net/floodlightcontroller/core/RoleInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..26c1a510bd556e5cedb5eb0b91f45e45ebe0e456 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/core/RoleInfo.java @@ -0,0 +1,76 @@ +/** + * Copyright 2013, Big Switch Networks, Inc. + * + * 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; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import net.floodlightcontroller.core.IFloodlightProviderService.Role; + +import org.codehaus.jackson.annotate.JsonProperty; + + +public class RoleInfo { + protected String role; + protected String roleChangeDescription; + protected Date roleChangeDateTime; + + public RoleInfo() { + } + + public RoleInfo(String role) { + setRole(role); + } + + public RoleInfo(Role role, String description) { + this.role = (role != null) ? role.name() : "DISABLED"; + this.roleChangeDescription = description; + } + + public RoleInfo(Role role, String description, Date dt) { + this.role = (role != null) ? role.name() : "DISABLED"; + this.roleChangeDescription = description; + this.roleChangeDateTime = dt; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + @JsonProperty(value="change-description") + public String getRoleChangeDescription() { + return roleChangeDescription; + } + @JsonProperty(value="change-description") + public void setRoleChangeDescription(String roleChangeDescription) { + this.roleChangeDescription = roleChangeDescription; + } + @JsonProperty(value="change-date-time") + public String getRoleChangeDateTime() { + SimpleDateFormat formatter = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + formatter.setTimeZone(TimeZone.getTimeZone("UTC")); + return roleChangeDateTime == null ? + "" : formatter.format(roleChangeDateTime); + } + +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 1a41b9bb7a6c1b5a8850becf8629fd8a358aa278..607835b71a36ed3c01addf25894f7dba9274548b 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -19,11 +19,14 @@ package net.floodlightcontroller.core.internal; import java.io.FileInputStream; import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; import java.net.InetSocketAddress; import java.nio.channels.ClosedChannelException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -55,6 +58,7 @@ import net.floodlightcontroller.core.IOFSwitchDriver; import net.floodlightcontroller.core.IOFSwitchFilter; import net.floodlightcontroller.core.IOFSwitchListener; import net.floodlightcontroller.core.OFSwitchBase; +import net.floodlightcontroller.core.RoleInfo; import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.annotations.LogMessageDocs; import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState; @@ -176,13 +180,12 @@ public class Controller implements IFloodlightProviderService, // Configuration options protected int openFlowPort = 6633; protected int workerThreads = 0; - // The id for this controller node. Should be unique for each controller - // node in a controller cluster. - protected String controllerId = "localhost"; // The current role of the controller. // If the controller isn't configured to support roles, then this is null. protected Role role; + protected String lastRoleChangeDescription = "Controller startup."; + protected Date roleChangeDateTime = new Date(); // This is the role of the controller based on HARoleChange notifications // we have sent. I.e., this field reflects the last role notification // we have sent to the listeners. On a transition to slave we first set @@ -191,14 +194,13 @@ public class Controller implements IFloodlightProviderService, // OF messages while the modules are in slave role. // The pendingRole is a role change just received, but not sent out // notifications yet. - protected Role pendingRole;protected volatile Role notifiedRole; + protected Role pendingRole; + protected String pendRoleChangeDescription; + protected volatile Role notifiedRole; // A helper that handles sending and timeout handling for role requests protected RoleChanger roleChanger; protected SingletonTask roleChangeDamper; - // Start time of the controller - protected long systemStartTime; - // Flag to always flush flow table on switch reconnect (HA or otherwise) protected boolean alwaysClearFlowsOnSwAdd = false; @@ -385,12 +387,20 @@ public class Controller implements IFloodlightProviderService, } @Override - public void setRole(Role role) { + public RoleInfo getRoleInfo() { + synchronized(roleChanger) { + return new RoleInfo(role, lastRoleChangeDescription, roleChangeDateTime); + } + } + + @Override + public void setRole(Role role, String roleChangeDescription) { if (role == null) throw new NullPointerException("Role can not be null."); // If role is changed in quick succession for some reason, // the 2 second delay will dampen the frequency. this.pendingRole = role; + pendRoleChangeDescription = roleChangeDescription; roleChangeDamper.reschedule(2000, TimeUnit.MILLISECONDS); } @@ -408,6 +418,9 @@ public class Controller implements IFloodlightProviderService, Role oldRole = this.role; this.role = pendingRole; + this.lastRoleChangeDescription = this.pendRoleChangeDescription; + this.pendRoleChangeDescription = null; + this.roleChangeDateTime = new Date(); log.debug("Submitting role change request to role {}", role); roleChanger.submitRequest(connectedSwitches, role); @@ -987,9 +1000,9 @@ public class Controller implements IFloodlightProviderService, if (roleChanger.checkFirstPendingRoleRequestXid( sw, error.getXid())) { roleChanger.deliverRoleRequestError(sw, error); - } else if (error.getErrorCode() == + } else if (error.getErrorType() == OFErrorType.OFPET_BAD_REQUEST.getValue() && - error.getErrorType() == + error.getErrorCode() == OFBadRequestCode.OFPBRC_EPERM.ordinal() && role.equals(Role.MASTER)) { // We are the master and the switch returned permission @@ -1235,7 +1248,10 @@ public class Controller implements IFloodlightProviderService, } pktinProcTime.recordEndTimePktIn(sw, m, bc); } else { - log.warn("Unhandled OF Message: {} from {}", m, sw); + if (m.getType() != OFType.BARRIER_REPLY) + log.warn("Unhandled OF Message: {} from {}", m, sw); + else + log.debug("Received a Barrier Reply, no listeners for it"); } if ((bContext == null) && (bc != null)) flcontext_free(bc); @@ -1586,23 +1602,10 @@ public class Controller implements IFloodlightProviderService, return factory; } - @Override - public String getControllerId() { - return controllerId; - } - // ************** // Initialization // ************** - protected void updateControllerInfo() { - // Write out the controller info to the storage source - Map<String, Object> controllerInfo = new HashMap<String, Object>(); - String id = getControllerId(); - controllerInfo.put(CONTROLLER_ID, id); - storageSource.updateRow(CONTROLLER_TABLE_NAME, controllerInfo); - } - /** * Sets the initial role based on properties in the config params. @@ -1755,11 +1758,7 @@ public class Controller implements IFloodlightProviderService, this.workerThreads = Integer.parseInt(threads); } log.debug("Number of worker threads set to {}", this.workerThreads); - String controllerId = configParams.get("controllerid"); - if (controllerId != null) { - this.controllerId = controllerId; - } - log.debug("ControllerId set to {}", this.controllerId); + } private void initVendorMessages() { @@ -1794,7 +1793,6 @@ public class Controller implements IFloodlightProviderService, this.notifiedRole = this.role; this.roleChanger = new RoleChanger(this); initVendorMessages(); - this.systemStartTime = System.currentTimeMillis(); String option = configParams.get("flushSwitchesOnReconnect"); @@ -1824,20 +1822,6 @@ public class Controller implements IFloodlightProviderService, CONTROLLER_ID); storageSource.addListener(CONTROLLER_INTERFACE_TABLE_NAME, this); - while (true) { - try { - updateControllerInfo(); - break; - } - catch (StorageException e) { - log.info("Waiting for storage source"); - try { - Thread.sleep(1000); - } catch (InterruptedException e1) { - } - } - } - // Startup load monitoring if (overload_drop) { this.loadmonitor.startMonitoring( @@ -1989,7 +1973,8 @@ public class Controller implements IFloodlightProviderService, @Override public long getSystemStartTime() { - return (this.systemStartTime); + RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); + return rb.getStartTime(); } @Override @@ -2001,6 +1986,21 @@ public class Controller implements IFloodlightProviderService, return this.alwaysClearFlowsOnSwAdd; } + @Override + public Map<String, Long> getMemory() { + Map<String, Long> m = new HashMap<String, Long>(); + Runtime runtime = Runtime.getRuntime(); + m.put("total", runtime.totalMemory()); + m.put("free", runtime.freeMemory()); + return m; + } + + @Override + public Long getUptime() { + RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); + return rb.getUptime(); + } + @Override public void addOFSwitchDriver(String description, IOFSwitchDriver driver) { IOFSwitchDriver existingDriver = switchBindingMap.get(description); diff --git a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java index 003fbbc25fbfcd6e8755f078ab62bfdcdcc25217..e31c9cdae08403b59b3726f9aabbf3b4c5d33b79 100644 --- a/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/ControllerRoleResource.java @@ -20,6 +20,7 @@ import org.restlet.data.Status; import org.restlet.resource.ServerResource; import net.floodlightcontroller.core.IFloodlightProviderService; +import net.floodlightcontroller.core.RoleInfo; import net.floodlightcontroller.core.IFloodlightProviderService.Role; import net.floodlightcontroller.core.annotations.LogMessageDoc; @@ -37,7 +38,7 @@ public class ControllerRoleResource extends ServerResource { IFloodlightProviderService floodlightProvider = (IFloodlightProviderService)getContext().getAttributes(). get(IFloodlightProviderService.class.getCanonicalName()); - return new RoleInfo(floodlightProvider.getRole()); + return floodlightProvider.getRoleInfo(); } @Post("json") @@ -68,6 +69,6 @@ public class ControllerRoleResource extends ServerResource { (IFloodlightProviderService)getContext().getAttributes(). get(IFloodlightProviderService.class.getCanonicalName()); - floodlightProvider.setRole(role); + floodlightProvider.setRole(role, roleInfo.getRoleChangeDescription()); } } diff --git a/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java b/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java deleted file mode 100644 index c8fa0c1a49a6bf564dee230cfa38b7abbfca1c31..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/web/RoleInfo.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2013, Big Switch Networks, Inc. - * - * 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; - -import net.floodlightcontroller.core.IFloodlightProviderService.Role; - -public class RoleInfo { - protected String role; - - public RoleInfo() { - } - - public RoleInfo(String role) { - setRole(role); - } - - public RoleInfo(Role role) { - this.role = (role != null) ? role.name() : "DISABLED"; - } - - public String getRole() { - return role; - } - - public void setRole(String role) { - this.role = role; - } -} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java index 5da407b94ba0203274de890eaea89c3e31e47ffe..74a228eea5858a61cc50dee54aecdc310d5bd120 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SwitchRoleResource.java @@ -23,6 +23,7 @@ import org.restlet.resource.ServerResource; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.RoleInfo; import org.restlet.resource.Get; import org.slf4j.Logger; @@ -46,7 +47,7 @@ public class SwitchRoleResource extends ServerResource { HashMap<String,RoleInfo> model = new HashMap<String,RoleInfo>(); for (IOFSwitch sw: floodlightProvider.getSwitches().values()) { switchId = sw.getStringId(); - roleInfo = new RoleInfo(sw.getHARole()); + roleInfo = new RoleInfo(sw.getHARole(), null); model.put(switchId, roleInfo); } return model; @@ -56,7 +57,7 @@ public class SwitchRoleResource extends ServerResource { IOFSwitch sw = floodlightProvider.getSwitches().get(dpid); if (sw == null) return null; - roleInfo = new RoleInfo(sw.getHARole()); + roleInfo = new RoleInfo(sw.getHARole(), null); return roleInfo; } } diff --git a/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java b/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java index 978419873037467e4ad67439905fae510c24ea72..b6981ef252fc7c6c5a8ad69e39e3197ecf30c06c 100644 --- a/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java +++ b/src/main/java/net/floodlightcontroller/core/web/SystemUptimeResource.java @@ -23,25 +23,24 @@ import org.restlet.resource.ServerResource; public class SystemUptimeResource extends ServerResource { - - public class UptimeRest { - long systemUptimeMsec; - - public long getSystemUptimeMsec() { - return systemUptimeMsec; - } - } - - @Get("json") - public UptimeRest retrieve() { - IFloodlightProviderService floodlightProvider = - (IFloodlightProviderService)getContext().getAttributes(). - get(IFloodlightProviderService.class.getCanonicalName()); - - UptimeRest uptime = new UptimeRest(); - uptime.systemUptimeMsec = - System.currentTimeMillis() - floodlightProvider.getSystemStartTime(); - - return (uptime); - } + + public class UptimeRest { + long systemUptimeMsec; + + public long getSystemUptimeMsec() { + return systemUptimeMsec; + } + } + + @Get("json") + public UptimeRest retrieve() { + IFloodlightProviderService floodlightProvider = + (IFloodlightProviderService)getContext().getAttributes(). + get(IFloodlightProviderService.class.getCanonicalName()); + + UptimeRest uptime = new UptimeRest(); + uptime.systemUptimeMsec = floodlightProvider.getUptime(); + + return (uptime); + } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java index 69fac694665f14e6df08ada226d0a5496e2b9884..5e8f221a9829bdcf25d0f37a41fd33047db3affe 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java @@ -51,6 +51,12 @@ public interface IDeviceService extends IFloodlightService { public static final String CONTEXT_DST_DEVICE = "net.floodlightcontroller.devicemanager.dstDevice"; + /** + * The original destination device for the current packet-in + */ + public static final String CONTEXT_ORIG_DST_DEVICE = + "net.floodlightcontroller.devicemanager.origDstDevice"; + /** * A FloodlightContextStore object that can be used to interact with the * FloodlightContext information created by BVS manager. diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java index 6029af1b6c855460a45b502e4c4f568761fc648f..1b53550f6fe5f047db8160e4399e005c85e08dda 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IEntityClassListener.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 @@ -20,8 +20,8 @@ package net.floodlightcontroller.devicemanager; import java.util.Set; /** - * Implementors of this interface can receive updates from DeviceManager about - * the changes entity Classes. + * Implementors of this interface can receive updates from the Entity Classifier about + * the changes to entity Classes. * * @author Ananth Suryanarayana (Ananth.Suryanarayana@bigswitch.com) */ diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index d2b712de83d068685d87e1b6b1bcc1c49141a9c4..e6083f93acf39a4f356443e051d9ef3d4ee3177d 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -63,8 +63,6 @@ public class Device implements IDevice { // the vlan Ids from the entities of this device protected Short[] vlanIds; protected String dhcpClientName; - - /** * These are the old attachment points for the device that were @@ -75,6 +73,7 @@ public class Device implements IDevice { * The current attachment points for the device. */ protected List<AttachmentPoint> attachmentPoints; + // ************ // Constructors // ************ @@ -110,7 +109,7 @@ public class Device implements IDevice { if (deviceManager.isValidAttachmentPoint(sw, port)) { AttachmentPoint ap; ap = new AttachmentPoint(sw, port, -entity.getLastSeenTimestamp().getTime()); + entity.getLastSeenTimestamp().getTime()); this.attachmentPoints = new ArrayList<AttachmentPoint>(); this.attachmentPoints.add(ap); @@ -157,7 +156,7 @@ entity.getLastSeenTimestamp().getTime()); /** * 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 + * 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 @@ -172,23 +171,23 @@ entity.getLastSeenTimestamp().getTime()); 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, + 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, + System.arraycopy(device.entities, 0, this.entities, 0, insertionpoint); } if (insertionpoint < device.entities.length) { - // insertion point is not the end + // insertion point is not the end // copy from insertion point - System.arraycopy(device.entities, insertionpoint, + System.arraycopy(device.entities, insertionpoint, this.entities, insertionpoint+1, device.entities.length-insertionpoint); } @@ -216,7 +215,7 @@ entity.getLastSeenTimestamp().getTime()); this.entityClass = device.entityClass; computeVlandIds(); } - + private void computeVlandIds() { if (entities.length == 1) { if (entities[0].getVlan() != null) { @@ -224,6 +223,7 @@ entity.getLastSeenTimestamp().getTime()); } else { vlanIds = new Short[] { Short.valueOf((short)-1) }; } + return; } TreeSet<Short> vals = new TreeSet<Short>(); @@ -236,7 +236,6 @@ entity.getLastSeenTimestamp().getTime()); vlanIds = vals.toArray(new Short[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. @@ -419,7 +418,9 @@ entity.getLastSeenTimestamp().getTime()); if (apMap == null || apMap.isEmpty()) { apList.add(newAP); attachmentPoints = apList; - return true; + // there are no old attachment points - we should not treat this + // as a device moved. + return false; } long id = topology.getL2DomainId(sw); @@ -555,6 +556,9 @@ entity.getLastSeenTimestamp().getTime()); return false; } + // ******* + // IDevice + // ******* @Override public SwitchPort[] getAttachmentPoints() { @@ -568,16 +572,11 @@ entity.getLastSeenTimestamp().getTime()); if (attachmentPoints == null) return returnSwitchPorts; if (attachmentPoints.isEmpty()) return returnSwitchPorts; - // copy ap list. - List<AttachmentPoint> apList; - apList = new ArrayList<AttachmentPoint>(); - if (attachmentPoints != null) apList.addAll(attachmentPoints); - // get AP map. - Map<Long, AttachmentPoint> apMap = getAPMap(apList); + List<AttachmentPoint> apList = attachmentPoints; - if (apMap != null) { - for(AttachmentPoint ap: apMap.values()) { + if (apList != null) { + for(AttachmentPoint ap: apList) { SwitchPort swport = new SwitchPort(ap.getSw(), ap.getPort()); sp.add(swport); @@ -596,6 +595,8 @@ entity.getLastSeenTimestamp().getTime()); this.oldAPs = oldAPList; List<AttachmentPoint> dupList; + // get AP map. + Map<Long, AttachmentPoint> apMap = getAPMap(apList); dupList = this.getDuplicateAttachmentPoints(oldAPList, apMap); if (dupList != null) { for(AttachmentPoint ap: dupList) { @@ -608,10 +609,6 @@ entity.getLastSeenTimestamp().getTime()); return sp.toArray(new SwitchPort[sp.size()]); } - // ******* - // IDevice - // ******* - @Override public Long getDeviceKey() { return deviceKey; @@ -651,7 +648,7 @@ entity.getLastSeenTimestamp().getTime()); deviceManager.queryClassByEntity(entityClass, ipv4Fields, e); while (devices.hasNext()) { Device d = devices.next(); - if (deviceKey.equals(d.getDeviceKey())) + if (deviceKey.equals(d.getDeviceKey())) continue; for (Entity se : d.entities) { if (se.getIpv4Address() != null && @@ -678,7 +675,7 @@ entity.getLastSeenTimestamp().getTime()); public Short[] getSwitchPortVlanIds(SwitchPort swp) { TreeSet<Short> vals = new TreeSet<Short>(); for (Entity e : entities) { - if (e.switchDPID == swp.getSwitchDPID() + if (e.switchDPID == swp.getSwitchDPID() && e.switchPort == swp.getPort()) { if (e.getVlan() == null) vals.add(Ethernet.VLAN_UNTAGGED); diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 5796830ba388189cb15ee339bf4891c2a8cb6ce6..c58999b17733f2878b146633124483a2f73c0988 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -241,16 +241,16 @@ IFlowReconcileListener, IInfoProvider, IHAListener { return "DeviceUpdate [device=" + devIdStr + ", change=" + change + ", fieldsChanged=" + fieldsChanged + "]"; } - + } /** * AttachmentPointComparator - * + * * Compares two attachment points and returns the latest one. * It is assumed that the two attachment points are in the same * L2 domain. - * + * * @author srini */ protected class AttachmentPointComparator @@ -261,7 +261,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { @Override public int compare(AttachmentPoint oldAP, AttachmentPoint newAP) { - //First compare based on L2 domain ID; + //First compare based on L2 domain ID; long oldSw = oldAP.getSw(); short oldPort = oldAP.getPort(); @@ -362,7 +362,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { @Override public IDevice findClassDevice(IEntityClass entityClass, long macAddress, - Short vlan, Integer ipv4Address) + Short vlan, Integer ipv4Address) throws IllegalArgumentException { if (vlan != null && vlan.shortValue() <= 0) vlan = null; @@ -370,10 +370,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { ipv4Address = null; Entity e = new Entity(macAddress, vlan, ipv4Address, null, null, null); - if (entityClass == null || + if (entityClass == null || !allKeyFieldsPresent(e, entityClass.getKeyFields())) { throw new IllegalArgumentException("Not all key fields and/or " - + " no source device specified. Required fields: " + + + " no source device specified. Required fields: " + entityClassifier.getKeyFields()); } return findDestByEntity(entityClass, e); @@ -446,7 +446,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { ArrayList<Iterator<Device>> iterators = new ArrayList<Iterator<Device>>(); ClassState classState = getClassState(entityClass); - + DeviceIndex index = null; if (classState.secondaryIndexMap.size() > 0) { EnumSet<DeviceField> keys = @@ -454,7 +454,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { switchDPID, switchPort); index = classState.secondaryIndexMap.get(keys); } - + Iterator<Device> iter; if (index == null) { index = classState.classIndex; @@ -481,10 +481,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { index.queryByEntity(entity)); } iterators.add(iter); - + return new MultiIterator<Device>(iterators.iterator()); } - + protected Iterator<Device> getDeviceIteratorForQuery(Long macAddress, Short vlan, Integer ipv4Address, @@ -531,6 +531,16 @@ IFlowReconcileListener, IInfoProvider, IHAListener { logListeners(); } + @Override + public void addSuppressAPs(long swId, short port) { + suppressAPs.add(new SwitchPort(swId, port)); + } + + @Override + public void removeSuppressAPs(long swId, short port) { + suppressAPs.remove(new SwitchPort(swId, port)); + } + private void logListeners() { List<IDeviceListener> listeners = deviceListeners.getOrderedListeners(); if (listeners != null) { @@ -599,13 +609,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener { ListIterator<OFMatchReconcile> iter = ofmRcList.listIterator(); while (iter.hasNext()) { OFMatchReconcile ofm = iter.next(); - + // Remove the STOPPed flow. if (Command.STOP == reconcileFlow(ofm)) { iter.remove(); } } - + if (ofmRcList.size() > 0) { return Command.CONTINUE; } else { @@ -638,10 +648,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { fcStore.put(ofm.cntx, CONTEXT_DST_DEVICE, dstDevice); } if (logger.isTraceEnabled()) { - logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " + logger.trace("Reconciling flow: match={}, srcEntity={}, srcDev={}, " + "dstEntity={}, dstDev={}", new Object[] {ofm.ofmWithSwDpid.getOfMatch(), - srcEntity, srcDevice, + srcEntity, srcDevice, dstEntity, dstDevice } ); } return Command.CONTINUE; @@ -784,6 +794,14 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (srcEntity == null) return Command.STOP; + // Learn from ARP packet for special VRRP settings. + // In VRRP settings, the source MAC address and sender MAC + // addresses can be different. In such cases, we need to learn + // the IP to MAC mapping of the VRRP IP address. The source + // entity will not have that information. Hence, a separate call + // to learn devices in such cases. + learnDeviceFromArpResponseData(eth, sw.getId(), pi.getInPort()); + // Learn/lookup device information Device srcDevice = learnDeviceByEntity(srcEntity); if (srcDevice == null) @@ -860,8 +878,8 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } /** - * Get sender IP address from packet if the packet is either an ARP - * packet. + * Get sender IP address from packet if the packet is either an ARP + * packet. * @param eth * @param dlAddr * @return @@ -873,7 +891,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { (Ethernet.toLong(arp.getSenderHardwareAddress()) == dlAddr)) { return IPv4.toIPv4Address(arp.getSenderProtocolAddress()); } - } + } return 0; } @@ -904,6 +922,43 @@ IFlowReconcileListener, IInfoProvider, IHAListener { new Date()); } + /** + * Learn device from ARP data in scenarios where the + * Ethernet source MAC is different from the sender hardware + * address in ARP data. + */ + protected void learnDeviceFromArpResponseData(Ethernet eth, + long swdpid, + int port) { + + if (!(eth.getPayload() instanceof ARP)) return; + ARP arp = (ARP) eth.getPayload(); + + byte[] dlAddrArr = eth.getSourceMACAddress(); + long dlAddr = Ethernet.toLong(dlAddrArr); + + byte[] senderHardwareAddr = arp.getSenderHardwareAddress(); + long senderAddr = Ethernet.toLong(senderHardwareAddr); + + if (dlAddr == senderAddr) return; + + // Ignore broadcast/multicast source + if ((senderHardwareAddr[0] & 0x1) != 0) + return; + + short vlan = eth.getVlanID(); + int nwSrc = IPv4.toIPv4Address(arp.getSenderProtocolAddress()); + + Entity e = new Entity(senderAddr, + ((vlan >= 0) ? vlan : null), + ((nwSrc != 0) ? nwSrc : null), + swdpid, + port, + new Date()); + + learnDeviceByEntity(e); + } + /** * Get a (partial) entity for the destination from the packet. * @param eth @@ -954,7 +1009,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { Long swDpid = null; Short inPort = null; - + if (isSource) { swDpid = ofmWithSwDpid.getSwitchDataPathId(); inPort = ofmWithSwDpid.getOfMatch().getInputPort(); @@ -981,11 +1036,12 @@ IFlowReconcileListener, IInfoProvider, IHAListener { (learnap ? (int)inPort : null), new Date()); } + /** * Look up a {@link Device} based on the provided {@link Entity}. We first * check the primary index. If we do not find an entry there we classify - * the device into its IEntityClass and query the classIndex. - * This implies that all key field of the current IEntityClassifier must + * the device into its IEntityClass and query the classIndex. + * This implies that all key field of the current IEntityClassifier must * be present in the entity for the lookup to succeed! * @param entity the entity to search for * @return The {@link Device} object if found @@ -1021,18 +1077,19 @@ IFlowReconcileListener, IInfoProvider, IHAListener { * the given source device. The source device is important since * there could be ambiguity in the destination device without the * attachment point information. - * @param source the source device. The returned destination will be - * in the same entity class as the source. - * @param dstEntity the entity to look up + * @param reference the source device's entity class. + * The returned destination will be + * in the same entity class as the source. + * @param dstEntity the entity to look up * @return an {@link Device} or null if no device is found. */ protected Device findDestByEntity(IEntityClass reference, Entity dstEntity) { - - // Look up the fully-qualified entity to see if it + + // Look up the fully-qualified entity to see if it // exists in the primary entity index Long deviceKey = primaryIndex.findByEntity(dstEntity); - + if (deviceKey == null) { // This could happen because: // 1) no destination known, or a broadcast destination @@ -1051,7 +1108,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (deviceKey == null) return null; return deviceMap.get(deviceKey); } - /** * Look up a {@link Device} within a particular entity class based on @@ -1115,20 +1171,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener { // device map, and use the referenced Device below. device = deviceMap.get(deviceKey); if (device == null) { - // This can happen due to concurrent modification + // This can happen due to concurrent modification if (logger.isDebugEnabled()) { logger.debug("No device for deviceKey {} while " + "while processing entity {}", deviceKey, entity); } + // if so, then try again till we don't even get the device key + // and so we recreate the device + continue; } } else { // If the secondary index does not contain the entity, // create a new Device object containing the entity, and - // generate a new device ID if the the entity is on an - // attachment point port. Otherwise ignore. + // generate a new device ID if the the entity is on an + // attachment point port. Otherwise ignore. if (entity.hasSwitchPort() && - !topology.isAttachmentPointPort(entity.getSwitchDPID(), + !topology.isAttachmentPointPort(entity.getSwitchDPID(), entity.getSwitchPort().shortValue())) { if (logger.isDebugEnabled()) { logger.debug("Not learning new device on internal" @@ -1137,10 +1196,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { device = null; break; } - // Before we create the new device also check if + // Before we create the new device also check if // the entity is allowed (e.g., for spoofing protection) if (!isEntityAllowed(entity, entityClass)) { - logger.info("PacketIn is not allowed {} {}", + logger.info("PacketIn is not allowed {} {}", entityClass.getName(), entity); device = null; break; @@ -1174,23 +1233,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener { break; } - + // if it gets here, we have a pre-existing Device for this Entity if (!isEntityAllowed(entity, device.getEntityClass())) { - logger.info("PacketIn is not allowed {} {}", + logger.info("PacketIn is not allowed {} {}", device.getEntityClass().getName(), entity); return null; } - // If this is an internal port we don't learn the new entity - // and don't update indexes. We only learn on attachment point - // ports. + // If this is not an attachment point port we don't learn the new entity + // and don't update indexes. But we do allow the device to continue up + // the chain. if (entity.hasSwitchPort() && - !topology.isAttachmentPointPort(entity.getSwitchDPID(), + !topology.isAttachmentPointPort(entity.getSwitchDPID(), entity.getSwitchPort().shortValue())) { break; } int entityindex = -1; if ((entityindex = device.entityIndex(entity)) >= 0) { - // Entity already exists + // Entity already exists // update timestamp on the found entity Date lastSeen = entity.getLastSeenTimestamp(); if (lastSeen == null) { @@ -1198,15 +1257,14 @@ IFlowReconcileListener, IInfoProvider, IHAListener { entity.setLastSeenTimestamp(lastSeen); } device.entities[entityindex].setLastSeenTimestamp(lastSeen); - // we break the loop after the else block and after checking - // for new AP + // we break the loop after checking for changes to the AP } else { - // New entity for this devce + // New entity for this device // compute the insertion point for the entity. // see Arrays.binarySearch() entityindex = -(entityindex + 1); Device newDevice = allocateDevice(device, entity, entityindex); - + // generate updates EnumSet<DeviceField> changedFields = findChangedFields(device, entity); @@ -1227,23 +1285,23 @@ IFlowReconcileListener, IInfoProvider, IHAListener { updateSecondaryIndices(entity, device.getEntityClass(), deviceKey); - + if (changedFields.size() > 0) { deviceUpdates = updateUpdates(deviceUpdates, new DeviceUpdate(newDevice, CHANGE, changedFields)); } - // we break the loop after checking for changed AP + // we break the loop after checking for changed AP } - // Update attachment point (will only be hit if the device - // already existed and no concurrent modification + // Update attachment point (will only be hit if the device + // already existed and no concurrent modification) if (entity.hasSwitchPort()) { - boolean moved = + boolean moved = device.updateAttachmentPoint(entity.getSwitchDPID(), entity.getSwitchPort().shortValue(), entity.getLastSeenTimestamp().getTime()); - // TODO: use update mechanism instead of sending the + // TODO: use update mechanism instead of sending the // notification directly if (moved) { sendDeviceMovedNotification(device); @@ -1261,7 +1319,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } } } - break; + break; } if (deleteQueue != null) { @@ -1372,7 +1430,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { /** * Check if the entity e has all the keyFields set. Returns false if not - * @param e entity to check + * @param e entity to check * @param keyFields the key fields to check e against * @return */ @@ -1397,7 +1455,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { //if (e.vlan == null) return false; break; default: - // we should never get here. unless somebody extended + // we should never get here. unless somebody extended // DeviceFields throw new IllegalStateException(); } @@ -1475,23 +1533,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } } - // ********************* - // IEntityClassListener - // ********************* - @Override - public void entityClassChanged (Set<String> entityClassNames) { - /* iterate through the devices, reclassify the devices that belong - * to these entity class names - */ - Iterator<Device> diter = deviceMap.values().iterator(); - while (diter.hasNext()) { - Device d = diter.next(); - if (d.getEntityClass() == null || - entityClassNames.contains(d.getEntityClass().getName())) - reclassifyDevice(d); - } - } - /** * Clean up expired entities/devices */ @@ -1599,7 +1640,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { others); } } - + /** * method to delete a given device, remove all entities first and then * finally delete the device itself. @@ -1608,12 +1649,12 @@ IFlowReconcileListener, IInfoProvider, IHAListener { protected void deleteDevice(Device device) { ArrayList<Entity> emptyToKeep = new ArrayList<Entity>(); for (Entity entity : device.getEntities()) { - this.removeEntity(entity, device.getEntityClass(), + this.removeEntity(entity, device.getEntityClass(), device.getDeviceKey(), emptyToKeep); } if (!deviceMap.remove(device.getDeviceKey(), device)) { if (logger.isDebugEnabled()) - logger.debug("device map does not have this device -" + + logger.debug("device map does not have this device -" + device.toString()); } } @@ -1624,7 +1665,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { Long switchDPID, Integer switchPort) { // FIXME: vlan==null is a valid search. Need to handle this - // case correctly. Note that the code will still work correctly. + // case correctly. Note that the code will still work correctly. // But we might do a full device search instead of using an index. EnumSet<DeviceField> keys = EnumSet.noneOf(DeviceField.class); if (macAddress != null) keys.add(DeviceField.MAC); @@ -1635,7 +1676,6 @@ IFlowReconcileListener, IInfoProvider, IHAListener { return keys; } - protected Iterator<Device> queryClassByEntity(IEntityClass clazz, EnumSet<DeviceField> keyFields, Entity entity) { @@ -1667,16 +1707,17 @@ IFlowReconcileListener, IInfoProvider, IHAListener { int insertionpoint) { return new Device(device, entity, insertionpoint); } - + + //not used protected Device allocateDevice(Device device, Set <Entity> entities) { - List <AttachmentPoint> newPossibleAPs = + List <AttachmentPoint> newPossibleAPs = new ArrayList<AttachmentPoint>(); - List <AttachmentPoint> newAPs = + List <AttachmentPoint> newAPs = new ArrayList<AttachmentPoint>(); - for (Entity entity : entities) { + for (Entity entity : entities) { if (entity.switchDPID != null && entity.switchPort != null) { - AttachmentPoint aP = - new AttachmentPoint(entity.switchDPID.longValue(), + AttachmentPoint aP = + new AttachmentPoint(entity.switchDPID.longValue(), entity.switchPort.shortValue(), 0); newPossibleAPs.add(aP); } @@ -1697,15 +1738,9 @@ IFlowReconcileListener, IInfoProvider, IHAListener { return d; } - @Override - public void addSuppressAPs(long swId, short port) { - suppressAPs.add(new SwitchPort(swId, port)); - } - - @Override - public void removeSuppressAPs(long swId, short port) { - suppressAPs.remove(new SwitchPort(swId, port)); - } + // ********************* + // ITopologyListener + // ********************* /** * Topology listener method. @@ -1745,10 +1780,28 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } } } - + + // ********************* + // IEntityClassListener + // ********************* + + @Override + public void entityClassChanged (Set<String> entityClassNames) { + /* iterate through the devices, reclassify the devices that belong + * to these entity class names + */ + Iterator<Device> diter = deviceMap.values().iterator(); + while (diter.hasNext()) { + Device d = diter.next(); + if (d.getEntityClass() == null || + entityClassNames.contains(d.getEntityClass().getName())) + reclassifyDevice(d); + } + } + /** * this method will reclassify and reconcile a device - possibilities - * are - create new device(s), remove entities from this device. If the + * are - create new device(s), remove entities from this device. If the * device entity class did not change then it returns false else true. * @param device */ @@ -1761,10 +1814,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } boolean needToReclassify = false; for (Entity entity : device.entities) { - IEntityClass entityClass = + IEntityClass entityClass = this.entityClassifier.classifyEntity(entity); if (entityClass == null || device.getEntityClass() == null) { - needToReclassify = true; + needToReclassify = true; break; } if (!entityClass.getName(). @@ -1776,12 +1829,12 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (needToReclassify == false) { return false; } - + LinkedList<DeviceUpdate> deviceUpdates = new LinkedList<DeviceUpdate>(); // delete this device and then re-learn all the entities this.deleteDevice(device); - deviceUpdates.add(new DeviceUpdate(device, + deviceUpdates.add(new DeviceUpdate(device, DeviceUpdate.Change.DELETE, null)); if (!deviceUpdates.isEmpty()) processUpdates(deviceUpdates); @@ -1789,5 +1842,5 @@ IFlowReconcileListener, IInfoProvider, IHAListener { this.learnDeviceByEntity(entity); } return true; - } + } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java index 4f2d3f8c4995b466f2fc5e22edbf11160fbaf3ab..4811013e77258654c9577b0271a51f72c31bfd8d 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndex.java @@ -1,7 +1,7 @@ /** -* Copyright 2012 Big Switch Networks, Inc. +* Copyright 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 @@ -31,7 +31,7 @@ public class DeviceUniqueIndex extends DeviceIndex { /** * The index */ - private ConcurrentHashMap<IndexedEntity, Long> index; + private final ConcurrentHashMap<IndexedEntity, Long> index; /** * Construct a new device index using the provided key fields @@ -51,10 +51,10 @@ public class DeviceUniqueIndex extends DeviceIndex { final Long deviceKey = findByEntity(entity); if (deviceKey != null) return Collections.<Long>singleton(deviceKey).iterator(); - + return Collections.<Long>emptySet().iterator(); } - + @Override public Iterator<Long> getAll() { return index.values().iterator(); @@ -68,15 +68,15 @@ public class DeviceUniqueIndex extends DeviceIndex { Long ret = index.putIfAbsent(ie, deviceKey); if (ret != null && !ret.equals(deviceKey)) { - // If the return value is non-null, then fail the insert - // (this implies that a device using this entity has + // If the return value is non-null, then fail the insert + // (this implies that a device using this entity has // already been created in another thread). return false; } } return true; } - + @Override public void updateIndex(Entity entity, Long deviceKey) { IndexedEntity ie = new IndexedEntity(keyFields, entity); diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java index 4e2ffb1dbe11c3ce4d89fa110adbc42287c611e7..1ad6f4320866479f5f0ef3dcfb1001d3ef9e3c86 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.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 @@ -57,7 +57,7 @@ public interface ILinkDiscoveryService extends IFloodlightService { boolean isReverse); /** - * Returns an unmodifiable map from switch id to a set of all links with it + * Returns an unmodifiable map from switch id to a set of all links with it * as an endpoint. */ public Map<Long, Set<Link>> getSwitchLinks(); @@ -74,7 +74,8 @@ public interface ILinkDiscoveryService extends IFloodlightService { public Set<NodePortTuple> getSuppressLLDPsInfo(); /** - * Adds a switch port to suppress lldp set + * Adds a switch port to suppress lldp set. LLDPs and BDDPs will not be sent + * out, and if any are received on this port then they will be dropped. */ public void AddToSuppressLLDPs(long sw, short port); @@ -105,8 +106,13 @@ public interface ILinkDiscoveryService extends IFloodlightService { public Map<NodePortTuple, Set<Link>> getPortLinks(); /** + * addMACToIgnoreList is a service provided by LinkDiscovery to ignore + * certain packets early in the packet-in processing chain. Since LinkDiscovery + * is first in the packet-in processing pipeline, it can efficiently drop these + * packets. Currently these packets are identified only by their source MAC address. + * * Add a MAC address range to ignore list. All packet ins from this range - * will be dropped + * will be dropped - use with care! * @param mac The base MAC address that is to be ignored * @param ignoreBits The number of LSBs to ignore. A value of 0 will add * only one MAC address 'mac' to ignore list. A value of 48 will add diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index f35a556f621a38fdad5d442c5ec7ca63a0268edd..2e11e995deea7e12a938367937dfd35f182c9f43 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -143,7 +144,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, protected IRestApiService restApi; // LLDP and BDDP fields - private static final byte[] LLDP_STANDARD_DST_MAC_STRING = HexString.fromHexString("01:80:c2:00:00:0e"); + private static final byte[] LLDP_STANDARD_DST_MAC_STRING = + HexString.fromHexString("01:80:c2:00:00:0e"); private static final long LINK_LOCAL_MASK = 0xfffffffffff0L; private static final long LINK_LOCAL_VALUE = 0x0180c2000000L; protected static int EVENT_HISTORY_SIZE = 1024; // in seconds @@ -159,12 +161,12 @@ public class LinkDiscoveryManager implements IOFMessageListener, private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte private static final byte TLV_DIRECTION_VALUE_FORWARD[] = { 0x01 }; private static final byte TLV_DIRECTION_VALUE_REVERSE[] = { 0x02 }; - private static final LLDPTLV forwardTLV = new LLDPTLV().setType((byte) TLV_DIRECTION_TYPE) - .setLength((short) TLV_DIRECTION_LENGTH) + private static final LLDPTLV forwardTLV = new LLDPTLV().setType(TLV_DIRECTION_TYPE) + .setLength(TLV_DIRECTION_LENGTH) .setValue(TLV_DIRECTION_VALUE_FORWARD); - private static final LLDPTLV reverseTLV = new LLDPTLV().setType((byte) TLV_DIRECTION_TYPE) - .setLength((short) TLV_DIRECTION_LENGTH) + private static final LLDPTLV reverseTLV = new LLDPTLV().setType(TLV_DIRECTION_TYPE) + .setLength(TLV_DIRECTION_LENGTH) .setValue(TLV_DIRECTION_VALUE_REVERSE); // Link discovery task details. @@ -252,9 +254,123 @@ public class LinkDiscoveryManager implements IOFMessageListener, } protected Set<MACRange> ignoreMACSet; + //********************* + // ILinkDiscoveryService + //********************* + + @Override + public OFPacketOut generateLLDPMessage(long sw, short port, + boolean isStandard, boolean isReverse) { + + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + OFPhysicalPort ofpPort = iofSwitch.getPort(port); + + if (log.isTraceEnabled()) { + log.trace("Sending LLDP packet out of swich: {}, port: {}", + HexString.toHexString(sw), port); + } + + // using "nearest customer bridge" MAC address for broadest possible + // propagation + // through provider and TPMR bridges (see IEEE 802.1AB-2009 and + // 802.1Q-2011), + // in particular the Linux bridge which behaves mostly like a provider + // bridge + byte[] chassisId = new byte[] { 4, 0, 0, 0, 0, 0, 0 }; // filled in + // later + byte[] portId = new byte[] { 2, 0, 0 }; // filled in later + byte[] ttlValue = new byte[] { 0, 0x78 }; + // OpenFlow OUI - 00-26-E1 + byte[] dpidTLVValue = new byte[] { 0x0, 0x26, (byte) 0xe1, 0, 0, 0, + 0, 0, 0, 0, 0, 0 }; + LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127) + .setLength((short) dpidTLVValue.length) + .setValue(dpidTLVValue); + + byte[] dpidArray = new byte[8]; + ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray); + ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2); + + Long dpid = sw; + dpidBB.putLong(dpid); + // set the chassis id's value to last 6 bytes of dpid + System.arraycopy(dpidArray, 2, chassisId, 1, 6); + // set the optional tlv to the full dpid + System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8); + + // TODO: Consider remove this block of code. + // It's evil to overwrite port object. The the old code always + // overwrote mac address, we now only overwrite zero macs and + // log a warning, mostly for paranoia. + byte[] srcMac = ofpPort.getHardwareAddress(); + byte[] zeroMac = { 0, 0, 0, 0, 0, 0 }; + if (Arrays.equals(srcMac, zeroMac)) { + log.warn("Port {}/{} has zero hareware address" + + "overwrite with lower 6 bytes of dpid", + HexString.toHexString(dpid), ofpPort.getPortNumber()); + System.arraycopy(dpidArray, 2, srcMac, 0, 6); + } + + // set the portId to the outgoing port + portBB.putShort(port); + if (log.isTraceEnabled()) { + log.trace("Sending LLDP out of interface: {}/{}", + HexString.toHexString(sw), port); + } + + LLDP lldp = new LLDP(); + lldp.setChassisId(new LLDPTLV().setType((byte) 1) + .setLength((short) chassisId.length) + .setValue(chassisId)); + lldp.setPortId(new LLDPTLV().setType((byte) 2) + .setLength((short) portId.length) + .setValue(portId)); + lldp.setTtl(new LLDPTLV().setType((byte) 3) + .setLength((short) ttlValue.length) + .setValue(ttlValue)); + lldp.getOptionalTLVList().add(dpidTLV); + + // Add the controller identifier to the TLV value. + lldp.getOptionalTLVList().add(controllerTLV); + if (isReverse) { + lldp.getOptionalTLVList().add(reverseTLV); + } else { + lldp.getOptionalTLVList().add(forwardTLV); + } + + Ethernet ethernet; + if (isStandard) { + ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress()) + .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING) + .setEtherType(Ethernet.TYPE_LLDP); + ethernet.setPayload(lldp); + } else { + BSN bsn = new BSN(BSN.BSN_TYPE_BDDP); + bsn.setPayload(lldp); + + ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress()) + .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING) + .setEtherType(Ethernet.TYPE_BSN); + ethernet.setPayload(bsn); + } + + // serialize and wrap in a packet out + byte[] data = ethernet.serialize(); + OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory() + .getMessage(OFType.PACKET_OUT); + po.setBufferId(OFPacketOut.BUFFER_ID_NONE); + po.setInPort(OFPort.OFPP_NONE); + + // set data and data length + po.setLengthU(OFPacketOut.MINIMUM_LENGTH + data.length); + po.setPacketData(data); + + return po; + } + /** * Get the LLDP sending period in seconds. - * + * * @return LLDP sending period in seconds. */ public int getLldpFrequency() { @@ -263,17 +379,19 @@ public class LinkDiscoveryManager implements IOFMessageListener, /** * Get the LLDP timeout value in seconds - * + * * @return LLDP timeout value in seconds */ public int getLldpTimeout() { return LINK_TIMEOUT; } + @Override public Map<NodePortTuple, Set<Link>> getPortLinks() { return portLinks; } + @Override public Set<NodePortTuple> getSuppressLLDPsInfo() { return suppressLinkDiscovery; } @@ -282,6 +400,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, * Add a switch port to the suppressed LLDP list. Remove any known links on * the switch port. */ + @Override public void AddToSuppressLLDPs(long sw, short port) { NodePortTuple npt = new NodePortTuple(sw, port); this.suppressLinkDiscovery.add(npt); @@ -292,6 +411,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, * Remove a switch port from the suppressed LLDP list. Discover links on * that switchport. */ + @Override public void RemoveFromSuppressLLDPs(long sw, short port) { NodePortTuple npt = new NodePortTuple(sw, port); this.suppressLinkDiscovery.remove(npt); @@ -302,10 +422,12 @@ public class LinkDiscoveryManager implements IOFMessageListener, return shuttingDown; } + @Override public boolean isTunnelPort(long sw, short port) { return false; } + @Override public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) { if (info.getUnicastValidTime() != null) { return ILinkDiscovery.LinkType.DIRECT_LINK; @@ -315,809 +437,889 @@ public class LinkDiscoveryManager implements IOFMessageListener, return ILinkDiscovery.LinkType.INVALID_LINK; } - @LogMessageDoc(level = "ERROR", - message = "Error in link discovery updates loop", - explanation = "An unknown error occured while dispatching " - + "link update notifications", - recommendation = LogMessageDoc.GENERIC_ACTION) - private - void doUpdatesThread() throws InterruptedException { - do { - LDUpdate update = updates.take(); - List<LDUpdate> updateList = new ArrayList<LDUpdate>(); - updateList.add(update); - - // Add all the pending updates to the list. - while (updates.peek() != null) { - updateList.add(updates.remove()); - } + @Override + public Set<Short> getQuarantinedPorts(long sw) { + Set<Short> qPorts = new HashSet<Short>(); - if (linkDiscoveryAware != null) { - if (log.isTraceEnabled()) { - log.trace("Dispatching link discovery update {} {} {} {} {} for {}", - new Object[] { - update.getOperation(), - HexString.toHexString(update.getSrc()), - update.getSrcPort(), - HexString.toHexString(update.getDst()), - update.getDstPort(), - linkDiscoveryAware }); - } - try { - for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order - // maintained - lda.linkDiscoveryUpdate(updateList); - } - } catch (Exception e) { - log.error("Error in link discovery updates loop", e); - } + Iterator<NodePortTuple> iter = quarantineQueue.iterator(); + while (iter.hasNext()) { + NodePortTuple npt = iter.next(); + if (npt.getNodeId() == sw) { + qPorts.add(npt.getPortId()); } - } while (updates.peek() != null); + } + return qPorts; } - protected boolean isLinkDiscoverySuppressed(long sw, short portNumber) { - return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, - portNumber)); + @Override + public Map<Long, Set<Link>> getSwitchLinks() { + return this.switchLinks; } - protected void discoverLinks() { + @Override + public void addMACToIgnoreList(long mac, int ignoreBits) { + MACRange range = new MACRange(); + range.baseMAC = mac; + range.ignoreBits = ignoreBits; + ignoreMACSet.add(range); + } - // timeout known links. - timeoutLinks(); + @Override + public boolean isAutoPortFastFeature() { + return autoPortFastFeature; + } - // increment LLDP clock - lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL; + @Override + public void setAutoPortFastFeature(boolean autoPortFastFeature) { + this.autoPortFastFeature = autoPortFastFeature; + } - if (lldpClock == 0) { - log.debug("Sending LLDP out on all ports."); - discoverOnAllPorts(); - } + @Override + public void addListener(ILinkDiscoveryListener listener) { + linkDiscoveryAware.add(listener); } - /** - * Quarantine Ports. - */ - protected class QuarantineWorker implements Runnable { - @Override - public void run() { - try { - processBDDPLists(); - } catch (Exception e) { - log.error("Error in quarantine worker thread", e); - } finally { - bddpTask.reschedule(BDDP_TASK_INTERVAL, - TimeUnit.MILLISECONDS); - } + @Override + public Map<Link, LinkInfo> getLinks() { + lock.readLock().lock(); + Map<Link, LinkInfo> result; + try { + result = new HashMap<Link, LinkInfo>(links); + } finally { + lock.readLock().unlock(); } + return result; } - /** - * Add a switch port to the quarantine queue. Schedule the quarantine task - * if the quarantine queue was empty before adding this switch port. - * - * @param npt - */ - protected void addToQuarantineQueue(NodePortTuple npt) { - if (quarantineQueue.contains(npt) == false) - quarantineQueue.add(npt); + // currently not called + public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() { + return portBroadcastDomainLinks; } - /** - * Remove a switch port from the quarantine queue. - */ - protected void removeFromQuarantineQueue(NodePortTuple npt) { - // Remove all occurrences of the node port tuple from the list. - while (quarantineQueue.remove(npt)) - ; + @Override + public String getName() { + return "linkdiscovery"; } - /** - * Add a switch port to maintenance queue. - * - * @param npt - */ - protected void addToMaintenanceQueue(NodePortTuple npt) { - // TODO We are not checking if the switch port tuple is already - // in the maintenance list or not. This will be an issue for - // really large number of switch ports in the network. - if (maintenanceQueue.contains(npt) == false) - maintenanceQueue.add(npt); - } + //********************* + // OFMessage Listener + //********************* - /** - * Remove a switch port from maintenance queue. - * - * @param npt - */ - protected void removeFromMaintenanceQueue(NodePortTuple npt) { - // Remove all occurrences of the node port tuple from the queue. - while (maintenanceQueue.remove(npt)) - ; + @Override + public Command receive(IOFSwitch sw, OFMessage msg, + FloodlightContext cntx) { + switch (msg.getType()) { + case PACKET_IN: + return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, + cntx); + case PORT_STATUS: + return this.handlePortStatus(sw.getId(), (OFPortStatus) msg); + default: + break; + } + return Command.CONTINUE; } - /** - * This method processes the quarantine list in bursts. The task is at most - * once per BDDP_TASK_INTERVAL. One each call, BDDP_TASK_SIZE number of - * switch ports are processed. Once the BDDP packets are sent out through - * the switch ports, the ports are removed from the quarantine list. - */ + @Override + public boolean isCallbackOrderingPrereq(OFType type, String name) { + return false; + } - protected void processBDDPLists() { - int count = 0; - Set<NodePortTuple> nptList = new HashSet<NodePortTuple>(); + @Override + public boolean isCallbackOrderingPostreq(OFType type, String name) { + return false; + } - while (count < BDDP_TASK_SIZE && quarantineQueue.peek() != null) { - NodePortTuple npt; - npt = quarantineQueue.remove(); - sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, - false); - nptList.add(npt); - count++; - } + //*********************************** + // Internal Methods - Packet-in Processing Related + //*********************************** - count = 0; - while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) { - NodePortTuple npt; - npt = maintenanceQueue.remove(); - sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, - false); - count++; + protected Command handlePacketIn(long sw, OFPacketIn pi, + FloodlightContext cntx) { + Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + + if (eth.getEtherType() == Ethernet.TYPE_BSN) { + BSN bsn = (BSN) eth.getPayload(); + if (bsn == null) return Command.STOP; + if (bsn.getPayload() == null) return Command.STOP; + // It could be a packet other than BSN LLDP, therefore + // continue with the regular processing. + if (bsn.getPayload() instanceof LLDP == false) + return Command.CONTINUE; + return handleLldp((LLDP) bsn.getPayload(), sw, pi.getInPort(), false, cntx); + } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) { + return handleLldp((LLDP) eth.getPayload(), sw, pi.getInPort(), true, cntx); + } else if (eth.getEtherType() < 1500) { + long destMac = eth.getDestinationMAC().toLong(); + if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) { + if (log.isTraceEnabled()) { + log.trace("Ignoring packet addressed to 802.1D/Q " + + "reserved address."); + } + return Command.STOP; + } } - for (NodePortTuple npt : nptList) { - generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId()); + if (ignorePacketInFromSource(eth.getSourceMAC().toLong())) { + return Command.STOP; } - } - public Set<Short> getQuarantinedPorts(long sw) { - Set<Short> qPorts = new HashSet<Short>(); + // If packet-in is from a quarantine port, stop processing. + NodePortTuple npt = new NodePortTuple(sw, pi.getInPort()); + if (quarantineQueue.contains(npt)) return Command.STOP; - Iterator<NodePortTuple> iter = quarantineQueue.iterator(); - while (iter.hasNext()) { - NodePortTuple npt = iter.next(); - if (npt.getNodeId() == sw) { - qPorts.add(npt.getPortId()); + return Command.CONTINUE; + } + + private boolean ignorePacketInFromSource(long srcMAC) { + Iterator<MACRange> it = ignoreMACSet.iterator(); + while (it.hasNext()) { + MACRange range = it.next(); + long mask = ~0; + if (range.ignoreBits >= 0 && range.ignoreBits <= 48) { + mask = mask << range.ignoreBits; + if ((range.baseMAC & mask) == (srcMAC & mask)) { + return true; + } } } - return qPorts; + return false; } - private void generateSwitchPortStatusUpdate(long sw, short port) { - UpdateOperation operation; - + private Command handleLldp(LLDP lldp, long sw, short inPort, + boolean isStandard, FloodlightContext cntx) { + // If LLDP is suppressed on this port, ignore received packet as well IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - if (iofSwitch == null) return; - - OFPhysicalPort ofp = iofSwitch.getPort(port); - if (ofp == null) return; - - int srcPortState = ofp.getState(); - boolean portUp = ((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) != OFPortState.OFPPS_STP_BLOCK.getValue()); - - if (portUp) - operation = UpdateOperation.PORT_UP; - else - operation = UpdateOperation.PORT_DOWN; - - updates.add(new LDUpdate(sw, port, operation)); - } - /** - * Send LLDP on known ports - */ - protected void discoverOnKnownLinkPorts() { - // Copy the port set. - Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>(); - nptSet.addAll(portLinks.keySet()); + if (!isIncomingDiscoveryAllowed(sw, inPort, isStandard)) + return Command.STOP; - // Send LLDP from each of them. - for (NodePortTuple npt : nptSet) { - discover(npt); + // If this is a malformed LLDP exit + if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) { + return Command.STOP; } - } - - protected void discover(NodePortTuple npt) { - discover(npt.getNodeId(), npt.getPortId()); - } - protected void discover(long sw, short port) { - sendDiscoveryMessage(sw, port, true, false); - } + long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong(); + long otherId = 0; + boolean myLLDP = false; + Boolean isReverse = null; - /** - * This method is used to specifically ignore/consider specific - * links. - * @param src - * @param srcPort - * @param dst - * @param dstPort - * @return - */ - protected boolean isLinkAllowed(long src, short srcPort, - long dst, short dstPort) { - return true; - } + ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue()); + portBB.position(1); - /** - * Check if incoming discovery messages are enabled or not. - * @param sw - * @param port - * @param isStandard - * @return - */ - protected boolean isIncomingDiscoveryAllowed(long sw, short port, - boolean isStandard) { + Short remotePort = portBB.getShort(); + IOFSwitch remoteSwitch = null; - if (isLinkDiscoverySuppressed(sw, port)) { - /* Do not process LLDPs from this port as suppressLLDP is set */ - return false; + // Verify this LLDP packet matches what we're looking for + for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) { + if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 + && lldptlv.getValue()[0] == 0x0 + && lldptlv.getValue()[1] == 0x26 + && lldptlv.getValue()[2] == (byte) 0xe1 + && lldptlv.getValue()[3] == 0x0) { + ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue()); + remoteSwitch = floodlightProvider.getSwitches() + .get(dpidBB.getLong(4)); + } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8) { + otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong(); + if (myId == otherId) myLLDP = true; + } else if (lldptlv.getType() == TLV_DIRECTION_TYPE + && lldptlv.getLength() == TLV_DIRECTION_LENGTH) { + if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0]) + isReverse = false; + else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0]) + isReverse = true; + } } - IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - if (iofSwitch == null) { - return false; + if (myLLDP == false) { + // This is not the LLDP sent by this controller. + // If the LLDP message has multicast bit set, then we need to + // broadcast the packet as a regular packet (after checking IDs) + if (isStandard) { + if (log.isTraceEnabled()) { + log.trace("Got a standard LLDP=[{}] that was not sent by" + + " this controller. Not fowarding it.", lldp.toString()); + } + return Command.STOP; + } else if (myId < otherId) { + if (log.isTraceEnabled()) { + log.trace("Getting BDDP packets from a different controller" + + "and letting it go through normal processing chain."); + } + return Command.CONTINUE; + } + return Command.STOP; } - if (port == OFPort.OFPP_LOCAL.getValue()) return false; - - OFPhysicalPort ofpPort = iofSwitch.getPort(port); - if (ofpPort == null) { + if (remoteSwitch == null) { + // Ignore LLDPs not generated by Floodlight, or from a switch that + // has recently + // disconnected, or from a switch connected to another Floodlight + // instance if (log.isTraceEnabled()) { - log.trace("Null physical port. sw={}, port={}", - HexString.toHexString(sw), port); + log.trace("Received LLDP from remote switch not connected to the controller"); } - return false; - } - - return true; - } - - /** - * Check if outgoing discovery messages are enabled or not. - * @param sw - * @param port - * @param isStandard - * @param isReverse - * @return - */ - protected boolean isOutgoingDiscoveryAllowed(long sw, short port, - boolean isStandard, - boolean isReverse) { - - if (isLinkDiscoverySuppressed(sw, port)) { - /* Dont send LLDPs out of this port as suppressLLDP is set */ - return false; - } - - IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - if (iofSwitch == null) { - return false; + return Command.STOP; } - if (port == OFPort.OFPP_LOCAL.getValue()) return false; - - OFPhysicalPort ofpPort = iofSwitch.getPort(port); - if (ofpPort == null) { + if (!remoteSwitch.portEnabled(remotePort)) { if (log.isTraceEnabled()) { - log.trace("Null physical port. sw={}, port={}", - HexString.toHexString(sw), port); + log.trace("Ignoring link with disabled source port: switch {} port {}", + remoteSwitch.getStringId(), remotePort); } - return false; + return Command.STOP; + } + if (suppressLinkDiscovery.contains(new NodePortTuple( + remoteSwitch.getId(), + remotePort))) { + if (log.isTraceEnabled()) { + log.trace("Ignoring link with suppressed src port: switch {} port {}", + remoteSwitch.getStringId(), remotePort); + } + return Command.STOP; + } + if (!iofSwitch.portEnabled(inPort)) { + if (log.isTraceEnabled()) { + log.trace("Ignoring link with disabled dest port: switch {} port {}", + HexString.toHexString(sw), inPort); + } + return Command.STOP; } - // For fast ports, do not send forward LLDPs or BDDPs. - if (!isReverse && autoPortFastFeature && iofSwitch.isFastPort(port)) - return false; - return true; - } - - /** - * Get the actions for packet-out corresponding to a specific port. - * This is a placeholder for adding actions if any port-specific - * actions are desired. The default action is simply to output to - * the given port. - * @param port - * @return - */ - protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPhysicalPort port){ - // set actions - List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(new OFActionOutput(port.getPortNumber(), (short) 0)); - return actions; - } + OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort); + int srcPortState = (physicalPort != null) ? physicalPort.getState() + : 0; + physicalPort = iofSwitch.getPort(inPort); + int dstPortState = (physicalPort != null) ? physicalPort.getState() + : 0; - public OFPacketOut generateLLDPMessage(long sw, short port, - boolean isStandard, boolean isReverse) { + // Store the time of update to this link, and push it out to + // routingEngine + Link lt = new Link(remoteSwitch.getId(), remotePort, + iofSwitch.getId(), inPort); - IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - OFPhysicalPort ofpPort = iofSwitch.getPort(port); + if (!isLinkAllowed(lt.getSrc(), lt.getSrcPort(), + lt.getDst(), lt.getDstPort())) + return Command.STOP; - if (log.isTraceEnabled()) { - log.trace("Sending LLDP packet out of swich: {}, port: {}", - HexString.toHexString(sw), port); - return null; - } + // Continue only if link is allowed. + Long lastLldpTime = null; + Long lastBddpTime = null; - // using "nearest customer bridge" MAC address for broadest possible - // propagation - // through provider and TPMR bridges (see IEEE 802.1AB-2009 and - // 802.1Q-2011), - // in particular the Linux bridge which behaves mostly like a provider - // bridge - byte[] chassisId = new byte[] { 4, 0, 0, 0, 0, 0, 0 }; // filled in - // later - byte[] portId = new byte[] { 2, 0, 0 }; // filled in later - byte[] ttlValue = new byte[] { 0, 0x78 }; - // OpenFlow OUI - 00-26-E1 - byte[] dpidTLVValue = new byte[] { 0x0, 0x26, (byte) 0xe1, 0, 0, 0, - 0, 0, 0, 0, 0, 0 }; - LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127) - .setLength((short) dpidTLVValue.length) - .setValue(dpidTLVValue); + Long firstSeenTime = System.currentTimeMillis(); - byte[] dpidArray = new byte[8]; - ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray); - ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2); + if (isStandard) + lastLldpTime = System.currentTimeMillis(); + else + lastBddpTime = System.currentTimeMillis(); - Long dpid = sw; - dpidBB.putLong(dpid); - // set the chassis id's value to last 6 bytes of dpid - System.arraycopy(dpidArray, 2, chassisId, 1, 6); - // set the optional tlv to the full dpid - System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8); + LinkInfo newLinkInfo = new LinkInfo(firstSeenTime, lastLldpTime, + lastBddpTime, srcPortState, + dstPortState); - // TODO: Consider remove this block of code. - // It's evil to overwrite port object. The the old code always - // overwrote mac address, we now only overwrite zero macs and - // log a warning, mostly for paranoia. - byte[] srcMac = ofpPort.getHardwareAddress(); - byte[] zeroMac = { 0, 0, 0, 0, 0, 0 }; - if (Arrays.equals(srcMac, zeroMac)) { - log.warn("Port {}/{} has zero hareware address" - + "overwrite with lower 6 bytes of dpid", - HexString.toHexString(dpid), ofpPort.getPortNumber()); - System.arraycopy(dpidArray, 2, srcMac, 0, 6); - } + addOrUpdateLink(lt, newLinkInfo); - // set the portId to the outgoing port - portBB.putShort(port); - if (log.isTraceEnabled()) { - log.trace("Sending LLDP out of interface: {}/{}", - HexString.toHexString(sw), port); + // Check if reverse link exists. + // If it doesn't exist and if the forward link was seen + // first seen within a small interval, send probe on the + // reverse link. + newLinkInfo = links.get(lt); + if (newLinkInfo != null && isStandard && isReverse == false) { + Link reverseLink = new Link(lt.getDst(), lt.getDstPort(), + lt.getSrc(), lt.getSrcPort()); + LinkInfo reverseInfo = links.get(reverseLink); + if (reverseInfo == null) { + // the reverse link does not exist. + if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() + - LINK_TIMEOUT) { + this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), + isStandard, true); + } + } } - LLDP lldp = new LLDP(); - lldp.setChassisId(new LLDPTLV().setType((byte) 1) - .setLength((short) chassisId.length) - .setValue(chassisId)); - lldp.setPortId(new LLDPTLV().setType((byte) 2) - .setLength((short) portId.length) - .setValue(portId)); - lldp.setTtl(new LLDPTLV().setType((byte) 3) - .setLength((short) ttlValue.length) - .setValue(ttlValue)); - lldp.getOptionalTLVList().add(dpidTLV); - - // Add the controller identifier to the TLV value. - lldp.getOptionalTLVList().add(controllerTLV); - if (isReverse) { - lldp.getOptionalTLVList().add(reverseTLV); - } else { - lldp.getOptionalTLVList().add(forwardTLV); - } + // If the received packet is a BDDP packet, then create a reverse BDDP + // link as well. + if (!isStandard) { + Link reverseLink = new Link(lt.getDst(), lt.getDstPort(), + lt.getSrc(), lt.getSrcPort()); - Ethernet ethernet; - if (isStandard) { - ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress()) - .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING) - .setEtherType(Ethernet.TYPE_LLDP); - ethernet.setPayload(lldp); - } else { - BSN bsn = new BSN(BSN.BSN_TYPE_BDDP); - bsn.setPayload(lldp); + // srcPortState and dstPort state are reversed. + LinkInfo reverseInfo = new LinkInfo(firstSeenTime, lastLldpTime, + lastBddpTime, dstPortState, + srcPortState); - ethernet = new Ethernet().setSourceMACAddress(ofpPort.getHardwareAddress()) - .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING) - .setEtherType(Ethernet.TYPE_BSN); - ethernet.setPayload(bsn); + addOrUpdateLink(reverseLink, reverseInfo); } - // serialize and wrap in a packet out - byte[] data = ethernet.serialize(); - OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory() - .getMessage(OFType.PACKET_OUT); - po.setBufferId(OFPacketOut.BUFFER_ID_NONE); - po.setInPort(OFPort.OFPP_NONE); - - // set data and data length - po.setLengthU(OFPacketOut.MINIMUM_LENGTH + data.length); - po.setPacketData(data); + // Remove the node ports from the quarantine and maintenance queues. + NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), + lt.getSrcPort()); + NodePortTuple nptDst = new NodePortTuple(lt.getDst(), + lt.getDstPort()); + removeFromQuarantineQueue(nptSrc); + removeFromMaintenanceQueue(nptSrc); + removeFromQuarantineQueue(nptDst); + removeFromMaintenanceQueue(nptDst); - return po; + // Consume this message + return Command.STOP; } + //*********************************** + // Internal Methods - Port Status/ New Port Processing Related + //*********************************** + /** - * Send link discovery message out of a given switch port. The discovery - * message may be a standard LLDP or a modified LLDP, where the dst mac - * address is set to :ff. TODO: The modified LLDP will updated in the future - * and may use a different eth-type. + * Handles an OFPortStatus message from a switch. We will add or delete + * LinkTupes as well re-compute the topology if needed. * * @param sw - * @param port - * @param isStandard - * indicates standard or modified LLDP - * @param isReverse - * indicates whether the LLDP was sent as a response + * The IOFSwitch that sent the port status message + * @param ps + * The OFPortStatus message + * @return The Command to continue or stop after we process this message */ - @LogMessageDoc(level = "ERROR", - message = "Failure sending LLDP out port {port} on switch {switch}", - explanation = "An I/O error occured while sending LLDP message " - + "to the switch.", - recommendation = LogMessageDoc.CHECK_SWITCH) - protected - void sendDiscoveryMessage(long sw, short port, - boolean isStandard, boolean isReverse) { - - // Takes care of all checks including null pointer checks. - if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse)) - return; + protected Command handlePortStatus(long sw, OFPortStatus ps) { IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - OFPhysicalPort ofpPort = iofSwitch.getPort(port); + if (iofSwitch == null) return Command.CONTINUE; if (log.isTraceEnabled()) { - log.trace("Sending LLDP packet out of swich: {}, port: {}", - HexString.toHexString(sw), port); - return; - } - OFPacketOut po = generateLLDPMessage(sw, port, isStandard, isReverse); - - // Add actions - List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort); - po.setActions(actions); - short actionLength = 0; - Iterator <OFAction> actionIter = actions.iterator(); - while (actionIter.hasNext()) { - actionLength += actionIter.next().getLength(); + log.trace("handlePortStatus: Switch {} port #{} reason {}; " + + "config is {} state is {}", + new Object[] { iofSwitch.getStringId(), + ps.getDesc().getPortNumber(), + ps.getReason(), + ps.getDesc().getConfig(), + ps.getDesc().getState() }); } - po.setActionsLength(actionLength); - // po already has the minimum length + data length set - // simply add the actions length to this. - po.setLengthU(po.getLengthU() + po.getActionsLength()); + short port = ps.getDesc().getPortNumber(); + NodePortTuple npt = new NodePortTuple(sw, port); + boolean linkDeleted = false; + boolean linkInfoChanged = false; - // send + lock.writeLock().lock(); try { - iofSwitch.write(po, null); - iofSwitch.flush(); - } catch (IOException e) { - log.error("Failure sending LLDP out port {} on switch {}", - new Object[] { port, iofSwitch.getStringId() }, e); + // if ps is a delete, or a modify where the port is down or + // configured down + if ((byte) OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() + || ((byte) OFPortReason.OFPPR_MODIFY.ordinal() == ps.getReason() && !portEnabled(ps.getDesc()))) { + deleteLinksOnPort(npt, "Port Status Changed"); + LDUpdate update = new LDUpdate(sw, port, + UpdateOperation.PORT_DOWN); + updates.add(update); + linkDeleted = true; + } else if (ps.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) { + // If ps is a port modification and the port state has changed + // that affects links in the topology + + if (this.portLinks.containsKey(npt)) { + for (Link lt : this.portLinks.get(npt)) { + LinkInfo linkInfo = links.get(lt); + assert (linkInfo != null); + Integer updatedSrcPortState = null; + Integer updatedDstPortState = null; + if (lt.getSrc() == npt.getNodeId() + && lt.getSrcPort() == npt.getPortId() + && (linkInfo.getSrcPortState() != ps.getDesc() + .getState())) { + updatedSrcPortState = ps.getDesc().getState(); + linkInfo.setSrcPortState(updatedSrcPortState); + } + if (lt.getDst() == npt.getNodeId() + && lt.getDstPort() == npt.getPortId() + && (linkInfo.getDstPortState() != ps.getDesc() + .getState())) { + updatedDstPortState = ps.getDesc().getState(); + linkInfo.setDstPortState(updatedDstPortState); + } + if ((updatedSrcPortState != null) + || (updatedDstPortState != null)) { + // The link is already known to link discovery + // manager and the status has changed, therefore + // send an LDUpdate. + UpdateOperation operation = getUpdateOperation(linkInfo.getSrcPortState(), + linkInfo.getDstPortState()); + updates.add(new LDUpdate(lt.getSrc(), + lt.getSrcPort(), + lt.getDst(), + lt.getDstPort(), + getLinkType(lt, + linkInfo), + operation)); + writeLinkToStorage(lt, linkInfo); + linkInfoChanged = true; + } + } + } + + UpdateOperation operation = getUpdateOperation(ps.getDesc() + .getState()); + updates.add(new LDUpdate(sw, port, operation)); + } + + if (!linkDeleted && !linkInfoChanged) { + if (log.isTraceEnabled()) { + log.trace("handlePortStatus: Switch {} port #{} reason {};" + + " no links to update/remove", + new Object[] { HexString.toHexString(sw), + ps.getDesc().getPortNumber(), + ps.getReason() }); + } + } + } finally { + lock.writeLock().unlock(); + } + + if (!linkDeleted) { + // Send LLDP right away when port state is changed for faster + // cluster-merge. If it is a link delete then there is not need + // to send the LLDPs right away and instead we wait for the LLDPs + // to be sent on the timer as it is normally done + // do it outside the write-lock + // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS); + processNewPort(npt.getNodeId(), npt.getPortId()); } + return Command.CONTINUE; } /** - * Send LLDPs to all switch-ports + * Process a new port. If link discovery is disabled on the port, then do + * nothing. If autoportfast feature is enabled and the port is a fast port, + * then do nothing. Otherwise, send LLDP message. Add the port to + * quarantine. + * + * @param sw + * @param p */ - protected void discoverOnAllPorts() { - if (log.isTraceEnabled()) { - log.trace("Sending LLDP packets out of all the enabled ports on switch {}"); + private void processNewPort(long sw, short p) { + if (isLinkDiscoverySuppressed(sw, p)) { + // Do nothing as link discovery is suppressed. + return; } - Set<Long> switches = floodlightProvider.getSwitches().keySet(); - // Send standard LLDPs - for (long sw : switches) { - IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - if (iofSwitch == null) continue; - if (iofSwitch.getEnabledPorts() != null) { - for (OFPhysicalPort ofp : iofSwitch.getEnabledPorts()) { - if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber())) - continue; - if (autoPortFastFeature - && iofSwitch.isFastPort(ofp.getPortNumber())) - continue; - // sends forward LLDP only non-fastports. - sendDiscoveryMessage(sw, ofp.getPortNumber(), true, - false); + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch == null) return; - // If the switch port is not alreayd in the maintenance - // queue, add it. - NodePortTuple npt = new NodePortTuple( - sw, - ofp.getPortNumber()); - addToMaintenanceQueue(npt); - } - } + if (autoPortFastFeature && iofSwitch.isFastPort(p)) { + // Do nothing as the port is a fast port. + return; + } + NodePortTuple npt = new NodePortTuple(sw, p); + discover(sw, p); + // if it is not a fast port, add it to quarantine. + if (!iofSwitch.isFastPort(p)) { + addToQuarantineQueue(npt); + } else { + // Add to maintenance queue to ensure that BDDP packets + // are sent out. + addToMaintenanceQueue(npt); } } - protected void setControllerTLV() { - // Setting the controllerTLVValue based on current nano time, - // controller's IP address, and the network interface object hash - // the corresponding IP address. + private boolean portEnabled(OFPhysicalPort port) { + if (port == null) return false; + if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) + return false; + if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0) + return false; + // Port STP state doesn't work with multiple VLANs, so ignore it for now + // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == + // OFPortState.OFPPS_STP_BLOCK.getValue()) + // return false; + return true; + } - final int prime = 7867; - InetAddress localIPAddress = null; - NetworkInterface localInterface = null; + //*********************************** + // Internal Methods - Discovery Related + //*********************************** - byte[] controllerTLVValue = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; // 8 - // byte - // value. - ByteBuffer bb = ByteBuffer.allocate(10); + @LogMessageDoc(level = "ERROR", + message = "Error in link discovery updates loop", + explanation = "An unknown error occured while dispatching " + + "link update notifications", + recommendation = LogMessageDoc.GENERIC_ACTION) + private void doUpdatesThread() throws InterruptedException { + do { + LDUpdate update = updates.take(); + List<LDUpdate> updateList = new ArrayList<LDUpdate>(); + updateList.add(update); - try { - localIPAddress = java.net.InetAddress.getLocalHost(); - localInterface = NetworkInterface.getByInetAddress(localIPAddress); - } catch (Exception e) { - e.printStackTrace(); - } + // Add all the pending updates to the list. + while (updates.peek() != null) { + updateList.add(updates.remove()); + } - long result = System.nanoTime(); - if (localIPAddress != null) - result = result - * prime - + IPv4.toIPv4Address(localIPAddress.getHostAddress()); - if (localInterface != null) - result = result * prime - + localInterface.hashCode(); - // set the first 4 bits to 0. - result = result & (0x0fffffffffffffffL); + if (linkDiscoveryAware != null) { + if (log.isTraceEnabled()) { + log.trace("Dispatching link discovery update {} {} {} {} {} for {}", + new Object[] { + update.getOperation(), + HexString.toHexString(update.getSrc()), + update.getSrcPort(), + HexString.toHexString(update.getDst()), + update.getDstPort(), + linkDiscoveryAware }); + } + try { + for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order + // maintained + lda.linkDiscoveryUpdate(updateList); + } + } catch (Exception e) { + log.error("Error in link discovery updates loop", e); + } + } + } while (updates.peek() != null); + } - bb.putLong(result); + protected boolean isLinkDiscoverySuppressed(long sw, short portNumber) { + return this.suppressLinkDiscovery.contains(new NodePortTuple(sw, + portNumber)); + } - bb.rewind(); - bb.get(controllerTLVValue, 0, 8); + protected void discoverLinks() { - this.controllerTLV = new LLDPTLV().setType((byte) 0x0c) - .setLength((short) controllerTLVValue.length) - .setValue(controllerTLVValue); - } + // timeout known links. + timeoutLinks(); - @Override - public String getName() { - return "linkdiscovery"; - } + // increment LLDP clock + lldpClock = (lldpClock + 1) % LLDP_TO_ALL_INTERVAL; - @Override - public Command receive(IOFSwitch sw, OFMessage msg, - FloodlightContext cntx) { - switch (msg.getType()) { - case PACKET_IN: - return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, - cntx); - case PORT_STATUS: - return this.handlePortStatus(sw.getId(), (OFPortStatus) msg); - default: - break; + if (lldpClock == 0) { + log.debug("Sending LLDP out on all ports."); + discoverOnAllPorts(); } - return Command.CONTINUE; } - private Command handleLldp(LLDP lldp, long sw, short inPort, - boolean isStandard, FloodlightContext cntx) { - // If LLDP is suppressed on this port, ignore received packet as well - IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - - if (!isIncomingDiscoveryAllowed(sw, inPort, isStandard)) - return Command.STOP; - - // If this is a malformed LLDP, or not from us, exit - if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) { - return Command.CONTINUE; + /** + * Quarantine Ports. + */ + protected class QuarantineWorker implements Runnable { + @Override + public void run() { + try { + processBDDPLists(); + } catch (Exception e) { + log.error("Error in quarantine worker thread", e); + } finally { + bddpTask.reschedule(BDDP_TASK_INTERVAL, + TimeUnit.MILLISECONDS); + } } + } - long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong(); - long otherId = 0; - boolean myLLDP = false; - Boolean isReverse = null; + /** + * Add a switch port to the quarantine queue. Schedule the quarantine task + * if the quarantine queue was empty before adding this switch port. + * + * @param npt + */ + protected void addToQuarantineQueue(NodePortTuple npt) { + if (quarantineQueue.contains(npt) == false) + quarantineQueue.add(npt); + } - ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue()); - portBB.position(1); + /** + * Remove a switch port from the quarantine queue. + */ + protected void removeFromQuarantineQueue(NodePortTuple npt) { + // Remove all occurrences of the node port tuple from the list. + while (quarantineQueue.remove(npt)) + ; + } - Short remotePort = portBB.getShort(); - IOFSwitch remoteSwitch = null; + /** + * Add a switch port to maintenance queue. + * + * @param npt + */ + protected void addToMaintenanceQueue(NodePortTuple npt) { + // TODO We are not checking if the switch port tuple is already + // in the maintenance list or not. This will be an issue for + // really large number of switch ports in the network. + if (maintenanceQueue.contains(npt) == false) + maintenanceQueue.add(npt); + } - // Verify this LLDP packet matches what we're looking for - for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) { - if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 - && lldptlv.getValue()[0] == 0x0 - && lldptlv.getValue()[1] == 0x26 - && lldptlv.getValue()[2] == (byte) 0xe1 - && lldptlv.getValue()[3] == 0x0) { - ByteBuffer dpidBB = ByteBuffer.wrap(lldptlv.getValue()); - remoteSwitch = floodlightProvider.getSwitches() - .get(dpidBB.getLong(4)); - } else if (lldptlv.getType() == 12 && lldptlv.getLength() == 8) { - otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong(); - if (myId == otherId) myLLDP = true; - } else if (lldptlv.getType() == TLV_DIRECTION_TYPE - && lldptlv.getLength() == TLV_DIRECTION_LENGTH) { - if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0]) - isReverse = false; - else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0]) - isReverse = true; - } - } + /** + * Remove a switch port from maintenance queue. + * + * @param npt + */ + protected void removeFromMaintenanceQueue(NodePortTuple npt) { + // Remove all occurrences of the node port tuple from the queue. + while (maintenanceQueue.remove(npt)) + ; + } - if (myLLDP == false) { - // This is not the LLDP sent by this controller. - // If the LLDP message has multicast bit set, then we need to - // broadcast - // the packet as a regular packet. - if (isStandard) { - if (log.isTraceEnabled()) { - log.trace("Got a standard LLDP=[{}]. Not fowarding it.", lldp.toString()); - } - return Command.STOP; - } else if (myId < otherId) { - if (log.isTraceEnabled()) { - log.trace("Getting BDDP packets from a different controller" - + "and letting it go through normal processing chain."); - } - return Command.CONTINUE; - } - } + /** + * This method processes the quarantine list in bursts. The task is at most + * once per BDDP_TASK_INTERVAL. One each call, BDDP_TASK_SIZE number of + * switch ports are processed. Once the BDDP packets are sent out through + * the switch ports, the ports are removed from the quarantine list. + */ + protected void processBDDPLists() { + int count = 0; + Set<NodePortTuple> nptList = new HashSet<NodePortTuple>(); - if (remoteSwitch == null) { - // Ignore LLDPs not generated by Floodlight, or from a switch that - // has recently - // disconnected, or from a switch connected to another Floodlight - // instance - if (log.isTraceEnabled()) { - log.trace("Received LLDP from remote switch not connected to the controller"); - } - return Command.STOP; + while (count < BDDP_TASK_SIZE && quarantineQueue.peek() != null) { + NodePortTuple npt; + npt = quarantineQueue.remove(); + sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, + false); + nptList.add(npt); + count++; } - if (!remoteSwitch.portEnabled(remotePort)) { - if (log.isTraceEnabled()) { - log.trace("Ignoring link with disabled source port: switch {} port {}", - remoteSwitch.getStringId(), remotePort); - } - return Command.STOP; - } - if (suppressLinkDiscovery.contains(new NodePortTuple( - remoteSwitch.getId(), - remotePort))) { - if (log.isTraceEnabled()) { - log.trace("Ignoring link with suppressed src port: switch {} port {}", - remoteSwitch.getStringId(), remotePort); - } - return Command.STOP; - } - if (!iofSwitch.portEnabled(inPort)) { - if (log.isTraceEnabled()) { - log.trace("Ignoring link with disabled dest port: switch {} port {}", - HexString.toHexString(sw), inPort); - } - return Command.STOP; + count = 0; + while (count < BDDP_TASK_SIZE && maintenanceQueue.peek() != null) { + NodePortTuple npt; + npt = maintenanceQueue.remove(); + sendDiscoveryMessage(npt.getNodeId(), npt.getPortId(), false, + false); + count++; } - OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort); - int srcPortState = (physicalPort != null) ? physicalPort.getState() - : 0; - physicalPort = iofSwitch.getPort(inPort); - int dstPortState = (physicalPort != null) ? physicalPort.getState() - : 0; + for (NodePortTuple npt : nptList) { + generateSwitchPortStatusUpdate(npt.getNodeId(), npt.getPortId()); + } + } - // Store the time of update to this link, and push it out to - // routingEngine - Link lt = new Link(remoteSwitch.getId(), remotePort, - iofSwitch.getId(), inPort); + private void generateSwitchPortStatusUpdate(long sw, short port) { + UpdateOperation operation; - if (!isLinkAllowed(lt.getSrc(), lt.getSrcPort(), - lt.getDst(), lt.getDstPort())) - return Command.STOP; + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch == null) return; - // Continue only if link is allowed. - Long lastLldpTime = null; - Long lastBddpTime = null; + OFPhysicalPort ofp = iofSwitch.getPort(port); + if (ofp == null) return; - Long firstSeenTime = System.currentTimeMillis(); + int srcPortState = ofp.getState(); + boolean portUp = ((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) + != OFPortState.OFPPS_STP_BLOCK.getValue()); - if (isStandard) - lastLldpTime = System.currentTimeMillis(); + if (portUp) + operation = UpdateOperation.PORT_UP; else - lastBddpTime = System.currentTimeMillis(); + operation = UpdateOperation.PORT_DOWN; - LinkInfo newLinkInfo = new LinkInfo(firstSeenTime, lastLldpTime, - lastBddpTime, srcPortState, - dstPortState); + updates.add(new LDUpdate(sw, port, operation)); + } + /** + * Send LLDP on known ports + */ + protected void discoverOnKnownLinkPorts() { + // Copy the port set. + Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>(); + nptSet.addAll(portLinks.keySet()); - addOrUpdateLink(lt, newLinkInfo); + // Send LLDP from each of them. + for (NodePortTuple npt : nptSet) { + discover(npt); + } + } - // Continue only if addOrUpdateLink was successful. + protected void discover(NodePortTuple npt) { + discover(npt.getNodeId(), npt.getPortId()); + } - // Check if reverse link exists. - // If it doesn't exist and if the forward link was seen - // first seen within a small interval, send probe on the - // reverse link. + protected void discover(long sw, short port) { + sendDiscoveryMessage(sw, port, true, false); + } - newLinkInfo = links.get(lt); - if (newLinkInfo != null && isStandard && isReverse == false) { - Link reverseLink = new Link(lt.getDst(), lt.getDstPort(), - lt.getSrc(), lt.getSrcPort()); - LinkInfo reverseInfo = links.get(reverseLink); - if (reverseInfo == null) { - // the reverse link does not exist. - if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - - LINK_TIMEOUT) { - this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), - isStandard, true); - } + /** + * Check if incoming discovery messages are enabled or not. + * @param sw + * @param port + * @param isStandard + * @return + */ + protected boolean isIncomingDiscoveryAllowed(long sw, short port, + boolean isStandard) { + + if (isLinkDiscoverySuppressed(sw, port)) { + /* Do not process LLDPs from this port as suppressLLDP is set */ + return false; + } + + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch == null) { + return false; + } + + if (port == OFPort.OFPP_LOCAL.getValue()) return false; + + OFPhysicalPort ofpPort = iofSwitch.getPort(port); + if (ofpPort == null) { + if (log.isTraceEnabled()) { + log.trace("Null physical port. sw={}, port={}", + HexString.toHexString(sw), port); } + return false; } - // If the received packet is a BDDP packet, then create a reverse BDDP - // link as well. - if (!isStandard) { - Link reverseLink = new Link(lt.getDst(), lt.getDstPort(), - lt.getSrc(), lt.getSrcPort()); + return true; + } - // srcPortState and dstPort state are reversed. - LinkInfo reverseInfo = new LinkInfo(firstSeenTime, lastLldpTime, - lastBddpTime, dstPortState, - srcPortState); + /** + * Check if outgoing discovery messages are enabled or not. + * @param sw + * @param port + * @param isStandard + * @param isReverse + * @return + */ + protected boolean isOutgoingDiscoveryAllowed(long sw, short port, + boolean isStandard, + boolean isReverse) { - addOrUpdateLink(reverseLink, reverseInfo); + if (isLinkDiscoverySuppressed(sw, port)) { + /* Dont send LLDPs out of this port as suppressLLDP is set */ + return false; } - // Remove the node ports from the quarantine and maintenance queues. - NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), - lt.getSrcPort()); - NodePortTuple nptDst = new NodePortTuple(lt.getDst(), - lt.getDstPort()); - removeFromQuarantineQueue(nptSrc); - removeFromMaintenanceQueue(nptSrc); - removeFromQuarantineQueue(nptDst); - removeFromMaintenanceQueue(nptDst); + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch == null) { + return false; + } - // Consume this message - return Command.STOP; + if (port == OFPort.OFPP_LOCAL.getValue()) return false; + + OFPhysicalPort ofpPort = iofSwitch.getPort(port); + if (ofpPort == null) { + if (log.isTraceEnabled()) { + log.trace("Null physical port. sw={}, port={}", + HexString.toHexString(sw), port); + } + return false; + } + + // For fast ports, do not send forward LLDPs or BDDPs. + if (!isReverse && autoPortFastFeature && iofSwitch.isFastPort(port)) + return false; + return true; } - protected Command handlePacketIn(long sw, OFPacketIn pi, - FloodlightContext cntx) { - Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, - IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + /** + * Get the actions for packet-out corresponding to a specific port. + * This is a placeholder for adding actions if any port-specific + * actions are desired. The default action is simply to output to + * the given port. + * @param port + * @return + */ + protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPhysicalPort port){ + // set actions + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(new OFActionOutput(port.getPortNumber(), (short) 0)); + return actions; + } - if (eth.getEtherType() == Ethernet.TYPE_BSN) { - BSN bsn = (BSN) eth.getPayload(); - if (bsn == null) return Command.STOP; - if (bsn.getPayload() == null) return Command.STOP; - // It could be a packet other than BSN LLDP, therefore - // continue with the regular processing. - if (bsn.getPayload() instanceof LLDP == false) - return Command.CONTINUE; - return handleLldp((LLDP) bsn.getPayload(), sw, pi.getInPort(), false, cntx); - } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) { - return handleLldp((LLDP) eth.getPayload(), sw, pi.getInPort(), true, cntx); - } else if (eth.getEtherType() < 1500) { - long destMac = eth.getDestinationMAC().toLong(); - if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE) { - if (log.isTraceEnabled()) { - log.trace("Ignoring packet addressed to 802.1D/Q " - + "reserved address."); - } - return Command.STOP; - } + /** + * Send link discovery message out of a given switch port. The discovery + * message may be a standard LLDP or a modified LLDP, where the dst mac + * address is set to :ff. TODO: The modified LLDP will updated in the future + * and may use a different eth-type. + * + * @param sw + * @param port + * @param isStandard + * indicates standard or modified LLDP + * @param isReverse + * indicates whether the LLDP was sent as a response + */ + @LogMessageDoc(level = "ERROR", + message = "Failure sending LLDP out port {port} on switch {switch}", + explanation = "An I/O error occured while sending LLDP message " + + "to the switch.", + recommendation = LogMessageDoc.CHECK_SWITCH) + protected void sendDiscoveryMessage(long sw, short port, + boolean isStandard, boolean isReverse) { + + // Takes care of all checks including null pointer checks. + if (!isOutgoingDiscoveryAllowed(sw, port, isStandard, isReverse)) + return; + + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + OFPhysicalPort ofpPort = iofSwitch.getPort(port); + + if (log.isTraceEnabled()) { + log.trace("Sending LLDP packet out of swich: {}, port: {}", + HexString.toHexString(sw), port); + } + OFPacketOut po = generateLLDPMessage(sw, port, isStandard, isReverse); + + // Add actions + List<OFAction> actions = getDiscoveryActions(iofSwitch, ofpPort); + po.setActions(actions); + short actionLength = 0; + Iterator <OFAction> actionIter = actions.iterator(); + while (actionIter.hasNext()) { + actionLength += actionIter.next().getLength(); } + po.setActionsLength(actionLength); - if (ignorePacketInFromSource(eth.getSourceMAC().toLong())) { - return Command.STOP; + // po already has the minimum length + data length set + // simply add the actions length to this. + po.setLengthU(po.getLengthU() + po.getActionsLength()); + + // send + try { + iofSwitch.write(po, null); + iofSwitch.flush(); + } catch (IOException e) { + log.error("Failure sending LLDP out port {} on switch {}", + new Object[] { port, iofSwitch.getStringId() }, e); + } + } + + /** + * Send LLDPs to all switch-ports + */ + protected void discoverOnAllPorts() { + if (log.isTraceEnabled()) { + log.trace("Sending LLDP packets out of all the enabled ports on switch {}"); } + Set<Long> switches = floodlightProvider.getSwitches().keySet(); + // Send standard LLDPs + for (long sw : switches) { + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch == null) continue; + if (iofSwitch.getEnabledPorts() != null) { + for (OFPhysicalPort ofp : iofSwitch.getEnabledPorts()) { + if (isLinkDiscoverySuppressed(sw, ofp.getPortNumber())) + continue; + if (autoPortFastFeature + && iofSwitch.isFastPort(ofp.getPortNumber())) + continue; - // If packet-in is from a quarantine port, stop processing. - NodePortTuple npt = new NodePortTuple(sw, pi.getInPort()); - if (quarantineQueue.contains(npt)) return Command.STOP; + // sends forward LLDP only non-fastports. + sendDiscoveryMessage(sw, ofp.getPortNumber(), true, + false); - return Command.CONTINUE; + // If the switch port is not alreayd in the maintenance + // queue, add it. + NodePortTuple npt = new NodePortTuple( + sw, + ofp.getPortNumber()); + addToMaintenanceQueue(npt); + } + } + } } protected UpdateOperation getUpdateOperation(int srcPortState, int dstPortState) { - boolean added = (((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) != OFPortState.OFPPS_STP_BLOCK.getValue()) && ((dstPortState & OFPortState.OFPPS_STP_MASK.getValue()) != OFPortState.OFPPS_STP_BLOCK.getValue())); + boolean added = (((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) + != OFPortState.OFPPS_STP_BLOCK.getValue()) && + ((dstPortState & OFPortState.OFPPS_STP_MASK.getValue()) + != OFPortState.OFPPS_STP_BLOCK.getValue())); if (added) return UpdateOperation.LINK_UPDATED; return UpdateOperation.LINK_REMOVED; @@ -1132,6 +1334,17 @@ public class LinkDiscoveryManager implements IOFMessageListener, return UpdateOperation.PORT_DOWN; } + //************************************ + // Internal Methods - Link Operations Related + //************************************ + + /** + * This method is used to specifically ignore/consider specific links. + */ + protected boolean isLinkAllowed(long src, short srcPort, + long dst, short dstPort) { + return true; + } protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) { NodePortTuple srcNpt, dstNpt; @@ -1142,8 +1355,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, // put the new info. if an old info exists, it will be returned. LinkInfo oldInfo = links.put(lt, newInfo); if (oldInfo != null - && oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime()) - newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime()); + && oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime()) + newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime()); if (log.isTraceEnabled()) { log.trace("addOrUpdateLink: {} {}", @@ -1161,30 +1374,26 @@ public class LinkDiscoveryManager implements IOFMessageListener, if (oldInfo == null) { // index it by switch source if (!switchLinks.containsKey(lt.getSrc())) - switchLinks.put(lt.getSrc(), - new HashSet<Link>()); + switchLinks.put(lt.getSrc(), new HashSet<Link>()); switchLinks.get(lt.getSrc()).add(lt); // index it by switch dest if (!switchLinks.containsKey(lt.getDst())) - switchLinks.put(lt.getDst(), - new HashSet<Link>()); + switchLinks.put(lt.getDst(), new HashSet<Link>()); switchLinks.get(lt.getDst()).add(lt); // index both ends by switch:port if (!portLinks.containsKey(srcNpt)) - portLinks.put(srcNpt, - new HashSet<Link>()); + portLinks.put(srcNpt, new HashSet<Link>()); portLinks.get(srcNpt).add(lt); if (!portLinks.containsKey(dstNpt)) - portLinks.put(dstNpt, - new HashSet<Link>()); + portLinks.put(dstNpt, new HashSet<Link>()); portLinks.get(dstNpt).add(lt); // Add to portNOFLinks if the unicast valid time is null if (newInfo.getUnicastValidTime() == null) - addLinkToBroadcastDomain(lt); + addLinkToBroadcastDomain(lt); writeLinkToStorage(lt, newInfo); updateOperation = UpdateOperation.LINK_UPDATED; @@ -1273,13 +1482,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, return linkChanged; } - public Map<Long, Set<Link>> getSwitchLinks() { - return this.switchLinks; - } - /** * Removes links from memory and storage. - * + * * @param links * The List of @LinkTuple to delete. */ @@ -1289,7 +1494,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, /** * Removes links from memory and storage. - * + * * @param links * The List of @LinkTuple to delete. */ @@ -1343,243 +1548,22 @@ public class LinkDiscoveryManager implements IOFMessageListener, removeLinkFromStorage(lt); // TODO Whenever link is removed, it has to checked if - // the switchports must be added to quarantine. - - if (log.isTraceEnabled()) { - log.trace("Deleted link {}", lt); - } - } - } finally { - if (updateList != null) linkUpdateList.addAll(updateList); - updates.addAll(linkUpdateList); - lock.writeLock().unlock(); - } - } - - /** - * Handles an OFPortStatus message from a switch. We will add or delete - * LinkTupes as well re-compute the topology if needed. - * - * @param sw - * The IOFSwitch that sent the port status message - * @param ps - * The OFPortStatus message - * @return The Command to continue or stop after we process this message - */ - protected Command handlePortStatus(long sw, OFPortStatus ps) { - - IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - if (iofSwitch == null) return Command.CONTINUE; - - if (log.isTraceEnabled()) { - log.trace("handlePortStatus: Switch {} port #{} reason {}; " - + "config is {} state is {}", - new Object[] { iofSwitch.getStringId(), - ps.getDesc().getPortNumber(), - ps.getReason(), - ps.getDesc().getConfig(), - ps.getDesc().getState() }); - } - - short port = ps.getDesc().getPortNumber(); - NodePortTuple npt = new NodePortTuple(sw, port); - boolean linkDeleted = false; - boolean linkInfoChanged = false; - - lock.writeLock().lock(); - try { - // if ps is a delete, or a modify where the port is down or - // configured down - if ((byte) OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() - || ((byte) OFPortReason.OFPPR_MODIFY.ordinal() == ps.getReason() && !portEnabled(ps.getDesc()))) { - deleteLinksOnPort(npt, "Port Status Changed"); - LDUpdate update = new LDUpdate(sw, port, - UpdateOperation.PORT_DOWN); - updates.add(update); - linkDeleted = true; - } else if (ps.getReason() == (byte) OFPortReason.OFPPR_MODIFY.ordinal()) { - // If ps is a port modification and the port state has changed - // that affects links in the topology - - if (this.portLinks.containsKey(npt)) { - for (Link lt : this.portLinks.get(npt)) { - LinkInfo linkInfo = links.get(lt); - assert (linkInfo != null); - Integer updatedSrcPortState = null; - Integer updatedDstPortState = null; - if (lt.getSrc() == npt.getNodeId() - && lt.getSrcPort() == npt.getPortId() - && (linkInfo.getSrcPortState() != ps.getDesc() - .getState())) { - updatedSrcPortState = ps.getDesc().getState(); - linkInfo.setSrcPortState(updatedSrcPortState); - } - if (lt.getDst() == npt.getNodeId() - && lt.getDstPort() == npt.getPortId() - && (linkInfo.getDstPortState() != ps.getDesc() - .getState())) { - updatedDstPortState = ps.getDesc().getState(); - linkInfo.setDstPortState(updatedDstPortState); - } - if ((updatedSrcPortState != null) - || (updatedDstPortState != null)) { - // The link is already known to link discovery - // manager and the status has changed, therefore - // send an LDUpdate. - UpdateOperation operation = getUpdateOperation(linkInfo.getSrcPortState(), - linkInfo.getDstPortState()); - updates.add(new LDUpdate(lt.getSrc(), - lt.getSrcPort(), - lt.getDst(), - lt.getDstPort(), - getLinkType(lt, - linkInfo), - operation)); - writeLinkToStorage(lt, linkInfo); - linkInfoChanged = true; - } - } - } - - UpdateOperation operation = getUpdateOperation(ps.getDesc() - .getState()); - updates.add(new LDUpdate(sw, port, operation)); - } - - if (!linkDeleted && !linkInfoChanged) { - if (log.isTraceEnabled()) { - log.trace("handlePortStatus: Switch {} port #{} reason {};" - + " no links to update/remove", - new Object[] { HexString.toHexString(sw), - ps.getDesc().getPortNumber(), - ps.getReason() }); - } - } - } finally { - lock.writeLock().unlock(); - } - - if (!linkDeleted) { - // Send LLDP right away when port state is changed for faster - // cluster-merge. If it is a link delete then there is not need - // to send the LLDPs right away and instead we wait for the LLDPs - // to be sent on the timer as it is normally done - // do it outside the write-lock - // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS); - processNewPort(npt.getNodeId(), npt.getPortId()); - } - return Command.CONTINUE; - } - - /** - * Process a new port. If link discovery is disabled on the port, then do - * nothing. If autoportfast feature is enabled and the port is a fast port, - * then do nothing. Otherwise, send LLDP message. Add the port to - * quarantine. - * - * @param sw - * @param p - */ - private void processNewPort(long sw, short p) { - if (isLinkDiscoverySuppressed(sw, p)) { - // Do nothing as link discovery is suppressed. - return; - } - - IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); - if (iofSwitch == null) return; - - if (autoPortFastFeature && iofSwitch.isFastPort(p)) { - // Do nothing as the port is a fast port. - return; - } - NodePortTuple npt = new NodePortTuple(sw, p); - discover(sw, p); - // if it is not a fast port, add it to quarantine. - if (!iofSwitch.isFastPort(p)) { - addToQuarantineQueue(npt); - } else { - // Add to maintenance queue to ensure that BDDP packets - // are sent out. - addToMaintenanceQueue(npt); - } - } - - /** - * We send out LLDP messages when a switch is added to discover the topology - * - * @param sw - * The IOFSwitch that connected to the controller - */ - @Override - public void addedSwitch(IOFSwitch sw) { - - if (sw.getEnabledPortNumbers() != null) { - for (Short p : sw.getEnabledPortNumbers()) { - processNewPort(sw.getId(), p); - } - } - // Update event history - evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None"); - LDUpdate update = new LDUpdate(sw.getId(), null, - UpdateOperation.SWITCH_UPDATED); - updates.add(update); - } - - /** - * When a switch disconnects we remove any links from our map and notify. - * - * @param The - * id of the switch - */ - @Override - public void removedSwitch(IOFSwitch iofSwitch) { - // Update event history - long sw = iofSwitch.getId(); - evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None"); - List<Link> eraseList = new ArrayList<Link>(); - lock.writeLock().lock(); - try { - if (switchLinks.containsKey(sw)) { - if (log.isTraceEnabled()) { - log.trace("Handle switchRemoved. Switch {}; removing links {}", - HexString.toHexString(sw), switchLinks.get(sw)); - } - - List<LDUpdate> updateList = new ArrayList<LDUpdate>(); - updateList.add(new LDUpdate(sw, null, - UpdateOperation.SWITCH_REMOVED)); - // add all tuples with an endpoint on this switch to erase list - eraseList.addAll(switchLinks.get(sw)); - - // Sending the updateList, will ensure the updates in this - // list will be added at the end of all the link updates. - // Thus, it is not necessary to explicitly add these updates - // to the queue. - deleteLinks(eraseList, "Switch Removed", updateList); - } else { - // Switch does not have any links. - updates.add(new LDUpdate(sw, null, - UpdateOperation.SWITCH_REMOVED)); + // the switchports must be added to quarantine. + + if (log.isTraceEnabled()) { + log.trace("Deleted link {}", lt); + } } } finally { + if (updateList != null) linkUpdateList.addAll(updateList); + updates.addAll(linkUpdateList); lock.writeLock().unlock(); } } - /** - * We don't react the port changed notifications here. we listen for - * OFPortStatus messages directly. Might consider using this notifier - * instead - */ - @Override - public void switchPortChanged(Long switchId) { - // no-op - } - /** * Delete links incident on a given switch port. - * + * * @param npt * @param reason */ @@ -1667,72 +1651,282 @@ public class LinkDiscoveryManager implements IOFMessageListener, } } - private boolean portEnabled(OFPhysicalPort port) { - if (port == null) return false; - if ((OFPortConfig.OFPPC_PORT_DOWN.getValue() & port.getConfig()) > 0) - return false; - if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0) - return false; - // Port STP state doesn't work with multiple VLANs, so ignore it for now - // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == - // OFPortState.OFPPS_STP_BLOCK.getValue()) - // return false; - return true; + protected void addLinkToBroadcastDomain(Link lt) { + + NodePortTuple srcNpt, dstNpt; + srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); + dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort()); + + if (!portBroadcastDomainLinks.containsKey(srcNpt)) + portBroadcastDomainLinks.put(srcNpt, + new HashSet<Link>()); + portBroadcastDomainLinks.get(srcNpt).add(lt); + + if (!portBroadcastDomainLinks.containsKey(dstNpt)) + portBroadcastDomainLinks.put(dstNpt, + new HashSet<Link>()); + portBroadcastDomainLinks.get(dstNpt).add(lt); } - public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() { - return portBroadcastDomainLinks; + protected void removeLinkFromBroadcastDomain(Link lt) { + + NodePortTuple srcNpt, dstNpt; + srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); + dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort()); + + if (portBroadcastDomainLinks.containsKey(srcNpt)) { + portBroadcastDomainLinks.get(srcNpt).remove(lt); + if (portBroadcastDomainLinks.get(srcNpt).isEmpty()) + portBroadcastDomainLinks.remove(srcNpt); + } + + if (portBroadcastDomainLinks.containsKey(dstNpt)) { + portBroadcastDomainLinks.get(dstNpt).remove(lt); + if (portBroadcastDomainLinks.get(dstNpt).isEmpty()) + portBroadcastDomainLinks.remove(dstNpt); + } + } + + //****************** + // Internal Helper Methods + //****************** + + protected void setControllerTLV() { + // Setting the controllerTLVValue based on current nano time, + // controller's IP address, and the network interface object hash + // the corresponding IP address. + + final int prime = 7867; + InetAddress localIPAddress = null; + NetworkInterface localInterface = null; + + byte[] controllerTLVValue = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; // 8 + // byte + // value. + ByteBuffer bb = ByteBuffer.allocate(10); + + try { + localIPAddress = java.net.InetAddress.getLocalHost(); + localInterface = NetworkInterface.getByInetAddress(localIPAddress); + } catch (Exception e) { + e.printStackTrace(); + } + + long result = System.nanoTime(); + if (localIPAddress != null) + result = result + * prime + + IPv4.toIPv4Address(localIPAddress.getHostAddress()); + if (localInterface != null) + result = result * prime + + localInterface.hashCode(); + // set the first 4 bits to 0. + result = result & (0x0fffffffffffffffL); + + bb.putLong(result); + + bb.rewind(); + bb.get(controllerTLVValue, 0, 8); + + this.controllerTLV = new LLDPTLV().setType((byte) 0x0c) + .setLength((short) controllerTLVValue.length) + .setValue(controllerTLVValue); } + //****************** + // IOFSwitchListener + //****************** + + /** + * We send out LLDP messages when a switch is added to discover the topology + * + * @param sw + * The IOFSwitch that connected to the controller + */ @Override - public Map<Link, LinkInfo> getLinks() { - lock.readLock().lock(); - Map<Link, LinkInfo> result; + public void addedSwitch(IOFSwitch sw) { + + if (sw.getEnabledPortNumbers() != null) { + for (Short p : sw.getEnabledPortNumbers()) { + processNewPort(sw.getId(), p); + } + } + // Update event history + evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None"); + LDUpdate update = new LDUpdate(sw.getId(), null, + UpdateOperation.SWITCH_UPDATED); + updates.add(update); + } + + /** + * When a switch disconnects we remove any links from our map and notify. + * + * @param The + * id of the switch + */ + @Override + public void removedSwitch(IOFSwitch iofSwitch) { + // Update event history + long sw = iofSwitch.getId(); + evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None"); + List<Link> eraseList = new ArrayList<Link>(); + lock.writeLock().lock(); try { - result = new HashMap<Link, LinkInfo>(links); + if (switchLinks.containsKey(sw)) { + if (log.isTraceEnabled()) { + log.trace("Handle switchRemoved. Switch {}; removing links {}", + HexString.toHexString(sw), switchLinks.get(sw)); + } + + List<LDUpdate> updateList = new ArrayList<LDUpdate>(); + updateList.add(new LDUpdate(sw, null, + UpdateOperation.SWITCH_REMOVED)); + // add all tuples with an endpoint on this switch to erase list + eraseList.addAll(switchLinks.get(sw)); + + // Sending the updateList, will ensure the updates in this + // list will be added at the end of all the link updates. + // Thus, it is not necessary to explicitly add these updates + // to the queue. + deleteLinks(eraseList, "Switch Removed", updateList); + } else { + // Switch does not have any links. + updates.add(new LDUpdate(sw, null, + UpdateOperation.SWITCH_REMOVED)); + } } finally { - lock.readLock().unlock(); + lock.writeLock().unlock(); + } + } + + /** + * We don't react the port changed notifications here. we listen for + * OFPortStatus messages directly. Might consider using this notifier + * instead + */ + @Override + public void switchPortChanged(Long switchId) { + // no-op + } + + //********************* + // Storage Listener + //********************* + /** + * Sets the IStorageSource to use for ITology + * + * @param storageSource + * the storage source to use + */ + public void setStorageSource(IStorageSourceService storageSource) { + this.storageSource = storageSource; + } + + /** + * Gets the storage source for this ITopology + * + * @return The IStorageSource ITopology is writing to + */ + public IStorageSourceService getStorageSource() { + return storageSource; + } + + @Override + public void rowsModified(String tableName, Set<Object> rowKeys) { + + if (tableName.equals(TOPOLOGY_TABLE_NAME)) { + readTopologyConfigFromStorage(); + return; + } + + Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches(); + ArrayList<IOFSwitch> updated_switches = new ArrayList<IOFSwitch>(); + for (Object key : rowKeys) { + Long swId = new Long(HexString.toLong((String) key)); + if (switches.containsKey(swId)) { + IOFSwitch sw = switches.get(swId); + boolean curr_status = sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH); + boolean new_status = false; + IResultSet resultSet = null; + + try { + resultSet = storageSource.getRow(tableName, key); + for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { + // In case of multiple rows, use the status in last row? + Map<String, Object> row = it.next().getRow(); + if (row.containsKey(SWITCH_CONFIG_CORE_SWITCH)) { + new_status = ((String) row.get(SWITCH_CONFIG_CORE_SWITCH)).equals("true"); + } + } + } finally { + if (resultSet != null) resultSet.close(); + } + + if (curr_status != new_status) { + updated_switches.add(sw); + } + } else { + if (log.isTraceEnabled()) { + log.trace("Update for switch which has no entry in switch " + + "list (dpid={}), a delete action.", + key); + } + } + } + + for (IOFSwitch sw : updated_switches) { + // Set SWITCH_IS_CORE_SWITCH to it's inverse value + if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) { + sw.removeAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH); + if (log.isTraceEnabled()) { + log.trace("SWITCH_IS_CORE_SWITCH set to False for {}", + sw); + } + updates.add(new LDUpdate(sw.getId(), + SwitchType.BASIC_SWITCH, + UpdateOperation.SWITCH_UPDATED)); + } else { + sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, + new Boolean(true)); + if (log.isTraceEnabled()) { + log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw); + } + updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH, + UpdateOperation.SWITCH_UPDATED)); + } } - return result; } - protected void addLinkToBroadcastDomain(Link lt) { - - NodePortTuple srcNpt, dstNpt; - srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); - dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort()); - - if (!portBroadcastDomainLinks.containsKey(srcNpt)) - portBroadcastDomainLinks.put(srcNpt, - new HashSet<Link>()); - portBroadcastDomainLinks.get(srcNpt).add(lt); - - if (!portBroadcastDomainLinks.containsKey(dstNpt)) - portBroadcastDomainLinks.put(dstNpt, - new HashSet<Link>()); - portBroadcastDomainLinks.get(dstNpt).add(lt); + @Override + public void rowsDeleted(String tableName, Set<Object> rowKeys) { + // Ignore delete events, the switch delete will do the + // right thing on it's own. + readTopologyConfigFromStorage(); } - protected void removeLinkFromBroadcastDomain(Link lt) { - NodePortTuple srcNpt, dstNpt; - srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); - dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort()); + //****************************** + // Internal methods - Config Related + //****************************** - if (portBroadcastDomainLinks.containsKey(srcNpt)) { - portBroadcastDomainLinks.get(srcNpt).remove(lt); - if (portBroadcastDomainLinks.get(srcNpt).isEmpty()) - portBroadcastDomainLinks.remove(srcNpt); - } + protected void readTopologyConfigFromStorage() { + IResultSet topologyResult = storageSource.executeQuery(TOPOLOGY_TABLE_NAME, + null, null, + null); - if (portBroadcastDomainLinks.containsKey(dstNpt)) { - portBroadcastDomainLinks.get(dstNpt).remove(lt); - if (portBroadcastDomainLinks.get(dstNpt).isEmpty()) - portBroadcastDomainLinks.remove(dstNpt); + if (topologyResult.next()) { + boolean apf = topologyResult.getBoolean(TOPOLOGY_AUTOPORTFAST); + autoPortFastFeature = apf; + } else { + this.autoPortFastFeature = AUTOPORTFAST_DEFAULT; } + + if (autoPortFastFeature) + log.info("Setting autoportfast feature to ON"); + else + log.info("Setting autoportfast feature to OFF"); } - // STORAGE METHODS /** * Deletes all links from storage */ @@ -1740,22 +1934,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, storageSource.deleteRowsAsync(LINK_TABLE_NAME, null); } - /** - * Gets the storage key for a LinkTuple - * - * @param lt - * The LinkTuple to get - * @return The storage key as a String - */ - private String getLinkId(Link lt) { - return HexString.toHexString(lt.getSrc()) + "-" + lt.getSrcPort() - + "-" + HexString.toHexString(lt.getDst()) + "-" - + lt.getDstPort(); - } - /** * Writes a LinkTuple and corresponding LinkInfo to storage - * + * * @param lt * The LinkTuple to write * @param linkInfo @@ -1823,6 +2004,17 @@ public class LinkDiscoveryManager implements IOFMessageListener, storageSource.updateRowAsync(LINK_TABLE_NAME, rowValues); } + /** + * Removes a link from storage using an asynchronous call. + * + * @param lt + * The LinkTuple to delete. + */ + protected void removeLinkFromStorage(Link lt) { + String id = getLinkId(lt); + storageSource.deleteRowAsync(LINK_TABLE_NAME, id); + } + public Long readLinkValidTime(Link lt) { // FIXME: We're not currently using this right now, but if we start // to use this again, we probably shouldn't use it in its current @@ -1838,9 +2030,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, resultSet = storageSource.executeQuery(LINK_TABLE_NAME, columns, new OperatorPredicate( - LINK_ID, - OperatorPredicate.Operator.EQ, - id), + LINK_ID, + OperatorPredicate.Operator.EQ, + id), null); if (resultSet.next()) validTime = resultSet.getLong(LINK_VALID_TIME); @@ -1851,171 +2043,44 @@ public class LinkDiscoveryManager implements IOFMessageListener, } /** - * Removes a link from storage using an asynchronous call. - * + * Gets the storage key for a LinkTuple + * * @param lt - * The LinkTuple to delete. - */ - protected void removeLinkFromStorage(Link lt) { - String id = getLinkId(lt); - storageSource.deleteRowAsync(LINK_TABLE_NAME, id); - } - - @Override - public void addListener(ILinkDiscoveryListener listener) { - linkDiscoveryAware.add(listener); - } - - /** - * Register a link discovery aware component - * - * @param linkDiscoveryAwareComponent - */ - public - void - addLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) { - // TODO make this a copy on write set or lock it somehow - this.linkDiscoveryAware.add(linkDiscoveryAwareComponent); - } - - /** - * Deregister a link discovery aware component - * - * @param linkDiscoveryAwareComponent - */ - public - void - removeLinkDiscoveryAware(ILinkDiscoveryListener linkDiscoveryAwareComponent) { - // TODO make this a copy on write set or lock it somehow - this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent); - } - - /** - * Sets the IStorageSource to use for ITology - * - * @param storageSource - * the storage source to use - */ - public void setStorageSource(IStorageSourceService storageSource) { - this.storageSource = storageSource; - } - - /** - * Gets the storage source for this ITopology - * - * @return The IStorageSource ITopology is writing to + * The LinkTuple to get + * @return The storage key as a String */ - public IStorageSourceService getStorageSource() { - return storageSource; - } - - @Override - public boolean isCallbackOrderingPrereq(OFType type, String name) { - return false; - } - - @Override - public boolean isCallbackOrderingPostreq(OFType type, String name) { - return false; - } - - @Override - public void rowsModified(String tableName, Set<Object> rowKeys) { - - if (tableName.equals(TOPOLOGY_TABLE_NAME)) { - readTopologyConfigFromStorage(); - return; - } - - Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches(); - ArrayList<IOFSwitch> updated_switches = new ArrayList<IOFSwitch>(); - for (Object key : rowKeys) { - Long swId = new Long(HexString.toLong((String) key)); - if (switches.containsKey(swId)) { - IOFSwitch sw = switches.get(swId); - boolean curr_status = sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH); - boolean new_status = false; - IResultSet resultSet = null; - - try { - resultSet = storageSource.getRow(tableName, key); - for (Iterator<IResultSet> it = resultSet.iterator(); it.hasNext();) { - // In case of multiple rows, use the status in last row? - Map<String, Object> row = it.next().getRow(); - if (row.containsKey(SWITCH_CONFIG_CORE_SWITCH)) { - new_status = ((String) row.get(SWITCH_CONFIG_CORE_SWITCH)).equals("true"); - } - } - } finally { - if (resultSet != null) resultSet.close(); - } - - if (curr_status != new_status) { - updated_switches.add(sw); - } - } else { - if (log.isTraceEnabled()) { - log.trace("Update for switch which has no entry in switch " - + "list (dpid={}), a delete action.", - (String) key); - } - } - } - - for (IOFSwitch sw : updated_switches) { - // Set SWITCH_IS_CORE_SWITCH to it's inverse value - if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) { - sw.removeAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH); - if (log.isTraceEnabled()) { - log.trace("SWITCH_IS_CORE_SWITCH set to False for {}", - sw); - } - updates.add(new LDUpdate(sw.getId(), - SwitchType.BASIC_SWITCH, - UpdateOperation.SWITCH_UPDATED)); - } else { - sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, - new Boolean(true)); - if (log.isTraceEnabled()) { - log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw); - } - updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH, - UpdateOperation.SWITCH_UPDATED)); - } - } - } - - @Override - public void rowsDeleted(String tableName, Set<Object> rowKeys) { - // Ignore delete events, the switch delete will do the - // right thing on it's own. - readTopologyConfigFromStorage(); + private String getLinkId(Link lt) { + return HexString.toHexString(lt.getSrc()) + "-" + lt.getSrcPort() + + "-" + HexString.toHexString(lt.getDst()) + "-" + + lt.getDstPort(); } - // IFloodlightModule classes + //*************** + // IFloodlightModule + //*************** @Override - public Collection<Class<? extends IFloodlightService>> - getModuleServices() { - Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); + public Collection<Class<? extends IFloodlightService>> getModuleServices() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); l.add(ILinkDiscoveryService.class); // l.add(ITopologyService.class); return l; } @Override - public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { - Map<Class<? extends IFloodlightService>, IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); + public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { + Map<Class<? extends IFloodlightService>, IFloodlightService> m = + new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); // We are the class that implements the service m.put(ILinkDiscoveryService.class, this); return m; } @Override - public Collection<Class<? extends IFloodlightService>> - getModuleDependencies() { - Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); + public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { + Collection<Class<? extends IFloodlightService>> l = + new ArrayList<Class<? extends IFloodlightService>>(); l.add(IFloodlightProviderService.class); l.add(IStorageSourceService.class); l.add(IThreadPoolService.class); @@ -2024,10 +2089,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, } @Override - public - void - init(FloodlightModuleContext context) - throws FloodlightModuleException { + public void init(FloodlightModuleContext context) + throws FloodlightModuleException { floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); storageSource = context.getServiceImpl(IStorageSourceService.class); threadPool = context.getServiceImpl(IThreadPoolService.class); @@ -2044,7 +2107,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, log.warn("Error event history size, using default of {} seconds", EVENT_HISTORY_SIZE); } log.debug("Event history size set to {}", EVENT_HISTORY_SIZE); - + // Set the autoportfast feature to false. this.autoPortFastFeature = AUTOPORTFAST_DEFAULT; @@ -2063,7 +2126,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, this.evHistTopologySwitch = new EventHistory<EventHistoryTopologySwitch>(EVENT_HISTORY_SIZE); this.evHistTopologyLink = new EventHistory<EventHistoryTopologyLink>(EVENT_HISTORY_SIZE); this.evHistTopologyCluster = new EventHistory<EventHistoryTopologyCluster>(EVENT_HISTORY_SIZE); - this.ignoreMACSet = new HashSet<MACRange>(); + this.ignoreMACSet = Collections.newSetFromMap( + new ConcurrentHashMap<MACRange,Boolean>()); } @Override @@ -2089,8 +2153,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, explanation = "An unknown error occured while sending LLDP " + "messages to switches.", recommendation = LogMessageDoc.CHECK_SWITCH) }) - public - void startUp(FloodlightModuleContext context) { + public void startUp(FloodlightModuleContext context) { // Create our storage tables if (storageSource == null) { log.error("No storage source found."); @@ -2191,7 +2254,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, // Topology Manager's Event History members and methods // **************************************************** - // Topology Manager event history + /** + * Topology Manager event history + */ public EventHistory<EventHistoryTopologySwitch> evHistTopologySwitch; public EventHistory<EventHistoryTopologyLink> evHistTopologyLink; public EventHistory<EventHistoryTopologyCluster> evHistTopologyCluster; @@ -2199,9 +2264,10 @@ public class LinkDiscoveryManager implements IOFMessageListener, public EventHistoryTopologyLink evTopoLink; public EventHistoryTopologyCluster evTopoCluster; - // Switch Added/Deleted - private void - evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) { + /** + * Switch Added/Deleted Events + */ + private void evHistTopoSwitch(IOFSwitch sw, EvAction actn, String reason) { if (evTopoSwitch == null) { evTopoSwitch = new EventHistoryTopologySwitch(); } @@ -2264,6 +2330,10 @@ public class LinkDiscoveryManager implements IOFMessageListener, evTopoCluster = evHistTopologyCluster.put(evTopoCluster, action); } + //********************* + // IInfoProvider + //********************* + @Override public Map<String, Object> getInfo(String type) { if (!"summary".equals(type)) return null; @@ -2278,7 +2348,10 @@ public class LinkDiscoveryManager implements IOFMessageListener, return info; } - // IHARoleListener + //*************** + // IHAListener + //*************** + @Override public void roleChanged(Role oldRole, Role newRole) { switch (newRole) { @@ -2311,60 +2384,10 @@ public class LinkDiscoveryManager implements IOFMessageListener, } @Override - public - void - controllerNodeIPsChanged(Map<String, String> curControllerNodeIPs, - Map<String, String> addedControllerNodeIPs, - Map<String, String> removedControllerNodeIPs) { + public void controllerNodeIPsChanged(Map<String, String> curControllerNodeIPs, + Map<String, String> addedControllerNodeIPs, + Map<String, String> removedControllerNodeIPs) { // ignore } - public boolean isAutoPortFastFeature() { - return autoPortFastFeature; - } - - public void setAutoPortFastFeature(boolean autoPortFastFeature) { - this.autoPortFastFeature = autoPortFastFeature; - } - - @Override - public void addMACToIgnoreList(long mac, int ignoreBits) { - MACRange range = new MACRange(); - range.baseMAC = mac; - range.ignoreBits = ignoreBits; - ignoreMACSet.add(range); - } - - private boolean ignorePacketInFromSource(long srcMAC) { - Iterator<MACRange> it = ignoreMACSet.iterator(); - while (it.hasNext()) { - MACRange range = it.next(); - long mask = ~0; - if (range.ignoreBits >= 0 && range.ignoreBits <= 48) { - mask = mask << range.ignoreBits; - if ((range.baseMAC & mask) == (srcMAC & mask)) { - return true; - } - } - } - return false; - } - - public void readTopologyConfigFromStorage() { - IResultSet topologyResult = storageSource.executeQuery(TOPOLOGY_TABLE_NAME, - null, null, - null); - - if (topologyResult.next()) { - boolean apf = topologyResult.getBoolean(TOPOLOGY_AUTOPORTFAST); - autoPortFastFeature = apf; - } else { - this.autoPortFastFeature = AUTOPORTFAST_DEFAULT; - } - - if (autoPortFastFeature) - log.info("Setting autoportfast feature to ON"); - else - log.info("Setting autoportfast feature to OFF"); - } } diff --git a/src/main/java/net/floodlightcontroller/packet/BasePacket.java b/src/main/java/net/floodlightcontroller/packet/BasePacket.java index 4ecfdedddd813b1fbd42a3ad1a2dbc48eafc45ed..eed530c03d2e8047774697bc70032d58eee63981 100644 --- a/src/main/java/net/floodlightcontroller/packet/BasePacket.java +++ b/src/main/java/net/floodlightcontroller/packet/BasePacket.java @@ -17,12 +17,16 @@ package net.floodlightcontroller.packet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * @author David Erickson (daviderickson@cs.stanford.edu) */ public abstract class BasePacket implements IPacket { + public static final Logger log = LoggerFactory.getLogger(BasePacket.class); protected IPacket parent; protected IPacket payload; @@ -113,4 +117,4 @@ public abstract class BasePacket implements IPacket { pkt.setParent(this.parent); return pkt; } -} \ No newline at end of file +} diff --git a/src/main/java/net/floodlightcontroller/packet/Ethernet.java b/src/main/java/net/floodlightcontroller/packet/Ethernet.java index 44a908118d9e649ec0421f5620684a9d967f6519..4fc2380762a5c71e019886cb6c0da82e6b84fc19 100644 --- a/src/main/java/net/floodlightcontroller/packet/Ethernet.java +++ b/src/main/java/net/floodlightcontroller/packet/Ethernet.java @@ -230,7 +230,7 @@ public class Ethernet extends BasePacket { @Override public IPacket deserialize(byte[] data, int offset, int length) { - if (length <= 0) + if (length <= 16) // Ethernet packet minium should be 60, this is reasonable return null; ByteBuffer bb = ByteBuffer.wrap(data, offset, length); if (this.destinationMACAddress == null) @@ -261,13 +261,23 @@ public class Ethernet extends BasePacket { Class<? extends IPacket> clazz = Ethernet.etherTypeClassMap.get(this.etherType); try { payload = clazz.newInstance(); + this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); } catch (Exception e) { - throw new RuntimeException("Error parsing payload for Ethernet packet", e); + if (log.isTraceEnabled()) { + log.trace("Failed to parse ethernet packet {}->{}" + + " payload as {}, treat as plain ethernet packet", + new Object[] {this.sourceMACAddress, + this.destinationMACAddress, + clazz.getClass().getName()}); + log.trace("Exception from parsing {}", e); + } + payload = new Data(); + this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); } } else { payload = new Data(); + this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); } - this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); this.payload.setParent(this); return this; } diff --git a/src/main/java/net/floodlightcontroller/packet/ICMP.java b/src/main/java/net/floodlightcontroller/packet/ICMP.java index 5431277f72061e47129d6b7a99ba757baae71a74..6b02b5bd502a5869e35a86579152417f39cd0698 100644 --- a/src/main/java/net/floodlightcontroller/packet/ICMP.java +++ b/src/main/java/net/floodlightcontroller/packet/ICMP.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,8 @@ package net.floodlightcontroller.packet; import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; /** * Implements ICMP packet format @@ -28,6 +30,24 @@ public class ICMP extends BasePacket { protected byte icmpCode; protected short checksum; + // The value is the number of bytes of padding + public static final Map<Byte, Short> paddingMap; + + public static final byte ECHO_REPLY = 0x0; + public static final byte ECHO_REQUEST = 0x8; + public static final byte TIME_EXCEEDED = 0xB; + public static final byte DESTINATION_UNREACHABLE = 0x3; + + public static final byte CODE_PORT_UNREACHABLE = 0x3; + + static { + paddingMap = new HashMap<Byte, Short>(); + ICMP.paddingMap.put(ICMP.ECHO_REPLY, (short) 0); + ICMP.paddingMap.put(ICMP.ECHO_REQUEST, (short) 0); + ICMP.paddingMap.put(ICMP.TIME_EXCEEDED, (short) 4); + ICMP.paddingMap.put(ICMP.DESTINATION_UNREACHABLE, (short) 4); + } + /** * @return the icmpType */ @@ -79,8 +99,13 @@ public class ICMP extends BasePacket { * -checksum : 0 * -length : 0 */ + @Override public byte[] serialize() { - int length = 4; + short padding = 0; + if (paddingMap.containsKey(this.icmpType)) + padding = paddingMap.get(this.icmpType); + + int length = 4 + padding; byte[] payloadData = null; if (payload != null) { payload.setParent(this); @@ -94,6 +119,9 @@ public class ICMP extends BasePacket { bb.put(this.icmpType); bb.put(this.icmpCode); bb.putShort(this.checksum); + for (int i = 0; i < padding; i++) + bb.put((byte) 0); + if (payloadData != null) bb.put(payloadData); @@ -161,7 +189,13 @@ public class ICMP extends BasePacket { this.icmpType = bb.get(); this.icmpCode = bb.get(); this.checksum = bb.getShort(); - + + short padding = 0; + if (paddingMap.containsKey(this.icmpType)) + padding = paddingMap.get(this.icmpType); + + bb.position(bb.position() + padding); + this.payload = new Data(); this.payload = payload.deserialize(data, bb.position(), bb.limit()-bb.position()); this.payload.setParent(this); diff --git a/src/main/java/net/floodlightcontroller/packet/IPv4.java b/src/main/java/net/floodlightcontroller/packet/IPv4.java index 76a140ed002bad4ab93db3488a771b2141223376..507cef88cb408c8af5c4301c3b5f925177061814 100644 --- a/src/main/java/net/floodlightcontroller/packet/IPv4.java +++ b/src/main/java/net/floodlightcontroller/packet/IPv4.java @@ -58,6 +58,7 @@ public class IPv4 extends BasePacket { protected byte[] options; protected boolean isTruncated; + protected boolean isFragment; /** * Default constructor that sets the version to 4. @@ -66,6 +67,7 @@ public class IPv4 extends BasePacket { super(); this.version = 4; isTruncated = false; + isFragment = false; } /** @@ -127,6 +129,14 @@ public class IPv4 extends BasePacket { this.isTruncated = isTruncated; } + public boolean isFragment() { + return isFragment; + } + + public void setFragment(boolean isFrag) { + this.isFragment = isFrag; + } + /** * @param identification the identification to set */ @@ -361,7 +371,8 @@ public class IPv4 extends BasePacket { } IPacket payload; - if (IPv4.protocolClassMap.containsKey(this.protocol)) { + isFragment = ((this.flags & 0x1) != 0) || (this.fragmentOffset != 0); + if (!isFragment && IPv4.protocolClassMap.containsKey(this.protocol)) { Class<? extends IPacket> clazz = IPv4.protocolClassMap.get(this.protocol); try { payload = clazz.newInstance(); @@ -369,6 +380,11 @@ public class IPv4 extends BasePacket { throw new RuntimeException("Error parsing payload for IPv4 packet", e); } } else { + if (log.isTraceEnabled() && isFragment) { + log.trace("IPv4 fragment detected {}->{}, forward using IP header only", + fromIPv4Address(this.sourceAddress), + fromIPv4Address(this.destinationAddress)); + } payload = new Data(); } int payloadLength = this.totalLength - this.headerLength * 4; @@ -493,7 +509,7 @@ public class IPv4 extends BasePacket { * @return The IP address separated into bytes. */ public static byte[] toIPv4AddressBytes(int ipAddress) { - return new byte[] { + return new byte[] { (byte)(ipAddress >>> 24), (byte)(ipAddress >>> 16), (byte)(ipAddress >>> 8), diff --git a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java index 5fbb38d748971197531c5046cbf913421916b1c8..ab09375486b9f61b2cfa98b83a5e9eeb483bd20b 100644 --- a/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/IRoutingDecision.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 @@ -29,18 +29,23 @@ public interface IRoutingDecision { /* * NONE: NO-OP, continue with the packet processing chain * DROP: Drop this packet and this flow - * FORWARD: Forward this packet, and this flow, to the first (and only device) in getDestinationDevices(), - * if the destination is not known at this time, initiate a discovery action for it (e.g. ARP) - * FORWARD_OR_FLOOD: Forward this packet, and this flow, to the first (and only device) in getDestinationDevices(), - * if the destination is not known at this time, flood this packet on the source switch - * MULTICAST: Multicast this packet to all the interfaces and devices attached + * FORWARD: Forward this packet, and this flow, to the first + * (and only device) in getDestinationDevices(), + * if the destination is not known at this time, + * initiate a discovery action for it (e.g. ARP) + * FORWARD_OR_FLOOD: Forward this packet, and this flow, to the first + * (and only device) in getDestinationDevices(), + * if the destination is not known at this time, + * flood this packet on the source switch + * MULTICAST: Multicast this packet to all the interfaces + * and devices attached */ NONE, DROP, FORWARD, FORWARD_OR_FLOOD, MULTICAST } - + public static final FloodlightContextStore<IRoutingDecision> rtStore = new FloodlightContextStore<IRoutingDecision>(); - public static final String CONTEXT_DECISION = + public static final String CONTEXT_DECISION = "net.floodlightcontroller.routing.decision"; public void addToContext(FloodlightContext cntx); @@ -54,4 +59,6 @@ public interface IRoutingDecision { public void setMulticastInterfaces(List<SwitchPort> lspt); public Integer getWildcards(); public void setWildcards(Integer wildcards); + public short getHardTimeout(); + public void setHardTimeout(short hardTimeout); } diff --git a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java index 5fff3a2251333bd62e3125b661f3a8378bdccbca..aa4b3dde823ab75a38671e53767294d1964012f3 100644 --- a/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java +++ b/src/main/java/net/floodlightcontroller/routing/RoutingDecision.java @@ -29,6 +29,7 @@ public class RoutingDecision implements IRoutingDecision { protected RoutingAction action; protected Integer wildcards; + protected short hardTimeout; protected SwitchPort srcPort; protected IDevice srcDevice; protected List<IDevice> destDevices; @@ -46,6 +47,7 @@ public class RoutingDecision implements IRoutingDecision { Collections.synchronizedList(new ArrayList<SwitchPort>()); this.action = action; this.wildcards = null; + this.hardTimeout = ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT; } @Override @@ -100,6 +102,16 @@ public class RoutingDecision implements IRoutingDecision { this.wildcards = wildcards; } + @Override + public short getHardTimeout() { + return hardTimeout; + } + + @Override + public void setHardTimeout(short hardTimeout) { + this.hardTimeout = hardTimeout; + } + @Override public void addToContext(FloodlightContext cntx) { rtStore.put(cntx, IRoutingDecision.CONTEXT_DECISION, this); diff --git a/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java b/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java index aae3962073e8b0aa6708b69d98e582bbd3cf5ae0..3033e99e4fd44eb799045b38f7b947ceddcf3c7b 100644 --- a/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java +++ b/src/main/java/net/floodlightcontroller/storage/AbstractStorageSource.java @@ -46,6 +46,7 @@ import net.floodlightcontroller.storage.web.StorageWebRoutable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + @LogMessageCategory("System Database") public abstract class AbstractStorageSource implements IStorageSourceService, IFloodlightModule { @@ -460,6 +461,9 @@ public abstract class AbstractStorageSource " storage listeners", recommendation=LogMessageDoc.GENERIC_ACTION) protected synchronized void notifyListeners(StorageSourceNotification notification) { + if (logger.isTraceEnabled()) { + logger.trace("Notifying storage listeneres: {}", notification); + } String tableName = notification.getTableName(); Set<Object> keys = notification.getKeys(); Set<IStorageSourceListener> tableListeners = listeners.get(tableName); diff --git a/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java b/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java index 8a69eca7f1920bd5995c451d90468314c8b45d20..ee4fac5ac0d49018b1bd7d4ecabd4366d4e621d4 100644 --- a/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java +++ b/src/main/java/net/floodlightcontroller/storage/memory/MemoryStorageSource.java @@ -70,7 +70,10 @@ public class MemoryStorageSource extends NoSqlStorageSource { Collection<Map<String,Object>> allRows = table.getAllRows(); for (Map<String,Object> row : allRows) { Object v = row.get(predicateColumnName); - if (value.equals(v)) { + if (value != null) { + if ((v != null) && value.equals(v)) + result.add(row); + } else if (v == null) { result.add(row); } } diff --git a/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java b/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java index d7e5f956b4ddee76c68c99cc802a2fa447b19a00..8d19fdd95bf29256e4612f55f2cef4c04d7558b5 100644 --- a/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java +++ b/src/main/java/net/floodlightcontroller/storage/nosql/NoSqlStorageSource.java @@ -59,7 +59,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { private Map<String, Map<String,ColumnIndexMode>> tableIndexedColumnMap = new HashMap<String,Map<String,ColumnIndexMode>>(); - abstract class NoSqlPredicate { + abstract static class NoSqlPredicate { public boolean incorporateComparison(String columnName, OperatorPredicate.Operator operator, Comparable<?> value, @@ -80,7 +80,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { } @SuppressWarnings({ "unchecked", "rawtypes" }) - class NoSqlRangePredicate extends NoSqlPredicate { + static class NoSqlRangePredicate extends NoSqlPredicate { NoSqlStorageSource storageSource; String tableName; String columnName; @@ -320,7 +320,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { } } - class NoSqlOperatorPredicate extends NoSqlPredicate { + static class NoSqlOperatorPredicate extends NoSqlPredicate { NoSqlStorageSource storageSource; String columnName; @@ -354,16 +354,19 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { } } - class NoSqlCompoundPredicate extends NoSqlPredicate { + static class NoSqlCompoundPredicate extends NoSqlPredicate { NoSqlStorageSource storageSource; CompoundPredicate.Operator operator; boolean negated; + String tableName; List<NoSqlPredicate> predicateList; - NoSqlCompoundPredicate(NoSqlStorageSource storageSource, CompoundPredicate.Operator operator, - boolean negated, List<NoSqlPredicate> predicateList) { + NoSqlCompoundPredicate(NoSqlStorageSource storageSource, String tableName, + CompoundPredicate.Operator operator, boolean negated, + List<NoSqlPredicate> predicateList) { this.storageSource = storageSource; + this.tableName = tableName; this.operator = operator; this.negated = negated; this.predicateList = predicateList; @@ -404,7 +407,7 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { } @SuppressWarnings({ "unchecked", "rawtypes" }) - class RowComparator implements Comparator<Map<String,Object>> { + static class RowComparator implements Comparator<Map<String,Object>> { private String primaryKeyName; public RowComparator(String primaryKeyName) { @@ -456,31 +459,48 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { } if (update2) { if (iterator2.hasNext()) { - row2 = iterator1.next(); + row2 = iterator2.next(); key2 = (Comparable<?>)row2.get(primaryKeyName); } else { row2 = null; } } + update1 = update2 = false; if (operator == CompoundPredicate.Operator.AND) { if ((row1 == null) || (row2 == null)) break; - if (key1.equals(key2)) + int result = ((Comparable)key1).compareTo(key2); + if (result == 0) { combinedRowList.add(row1); + update1 = update2 = true; + } else if (result < 0) { + update1 = true; + } else { + update2 = true; + } } else { - if (row1 == null) { - if (row2 == null) - break; - combinedRowList.add(row2); - } else if ((row2 == null) || (((Comparable)key1).compareTo(key2) <= 0)) { + if (row1 != null) { + if (row2 != null) { + int result = ((Comparable)key1).compareTo(key2); + if (result <= 0) { + combinedRowList.add(row1); + update1 = true; + update2 = (result == 0); + } else { + combinedRowList.add(row2); + update2 = true; + } + } else { + combinedRowList.add(row1); + update1 = true; + } + } else if (row2 != null) { combinedRowList.add(row2); + update2 = true; } else { - combinedRowList.add(row1); + break; } } - - update1 = (key2 == null) || (((Comparable)key1).compareTo(key2) <= 0); - update2 = (key1 == null) || (((Comparable)key2).compareTo(key1) <= 0); } return combinedRowList; @@ -488,15 +508,35 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { public List<Map<String,Object>> execute(String columnNames[]) { List<Map<String,Object>> combinedRowList = null; + Set<NoSqlPredicate> inefficientPredicates = new HashSet<NoSqlPredicate>(); for (NoSqlPredicate predicate: predicateList) { - List<Map<String,Object>> rowList = predicate.execute(columnNames); - if (combinedRowList != null) { - combinedRowList = combineRowLists("id", combinedRowList, rowList, operator); + if (predicate.canExecuteEfficiently()) { + List<Map<String,Object>> rowList = predicate.execute(columnNames); + if (combinedRowList != null) { + String primaryKeyName = storageSource.getTablePrimaryKeyName(tableName); + combinedRowList = combineRowLists(primaryKeyName, + combinedRowList, rowList, operator); + } else { + combinedRowList = rowList; + } } else { - combinedRowList = rowList; + inefficientPredicates.add(predicate); } } - return combinedRowList; + + if (inefficientPredicates.isEmpty()) + return combinedRowList; + + List<Map<String,Object>> filteredRowList = new ArrayList<Map<String,Object>>(); + for (Map<String,Object> row: combinedRowList) { + for (NoSqlPredicate predicate: inefficientPredicates) { + if (predicate.matchesRow(row)) { + filteredRowList.add(row); + } + } + } + + return filteredRowList; } public boolean matchesRow(Map<String,Object> row) { @@ -602,7 +642,8 @@ public abstract class NoSqlStorageSource extends AbstractStorageSource { noSqlPredicateList.add(noSqlPredicate); } } - convertedPredicate = new NoSqlCompoundPredicate(this, compoundPredicate.getOperator(), + convertedPredicate = new NoSqlCompoundPredicate(this, tableName, + compoundPredicate.getOperator(), compoundPredicate.isNegated(), noSqlPredicateList); } else if (predicate instanceof OperatorPredicate) { OperatorPredicate operatorPredicate = (OperatorPredicate) predicate; diff --git a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java index 7bab984abdc3b6bd79dbc007d8ad0c537d6d17bd..8711551b82e321b8aa261845dae3e24fefba3552 100644 --- a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java +++ b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java @@ -29,7 +29,8 @@ import org.openflow.util.HexString; * to the actual objects. * @author srini */ -public class NodePortTuple { + +public class NodePortTuple implements Comparable<NodePortTuple> { protected long nodeId; // switch DPID protected short portId; // switch port id @@ -93,7 +94,7 @@ public class NodePortTuple { return false; return true; } - + /** * API to return a String value formed wtih NodeID and PortID * The portID is a 16-bit field, so mask it as an integer to get full @@ -103,4 +104,23 @@ public class NodePortTuple { public String toKeyString() { return (HexString.toHexString(nodeId)+ "|" + (portId & 0xffff)); } + + @Override + public int compareTo(NodePortTuple obj) { + final int BEFORE = -1; + final int EQUAL = 0; + final int AFTER = 1; + + if (this.getNodeId() < obj.getNodeId()) + return BEFORE; + if (this.getNodeId() > obj.getNodeId()) + return AFTER; + + if (this.getPortId() < obj.getPortId()) + return BEFORE; + if (this.getPortId() > obj.getPortId()) + return AFTER; + + return EQUAL; + } } diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java index 7bc878614d124540d3165d42f7fa35088caf78e8..af49de70bbb6ca8f29426bc370d5b343fdc30442 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyInstance.java @@ -19,7 +19,6 @@ package net.floodlightcontroller.topology; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -37,10 +36,10 @@ import net.floodlightcontroller.routing.BroadcastTree; import net.floodlightcontroller.routing.Link; import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.routing.RouteId; -import net.floodlightcontroller.util.LRUHashMap; +import com.google.common.cache.*; /** - * A representation of a network topology. Used internally by + * A representation of a network topology. Used internally by * {@link TopologyManager} */ @LogMessageCategory("Network Topology") @@ -48,7 +47,7 @@ public class TopologyInstance { public static final short LT_SH_LINK = 1; public static final short LT_BD_LINK = 2; - public static final short LT_TUNNEL = 3; + public static final short LT_TUNNEL = 3; public static final int MAX_LINK_WEIGHT = 10000; public static final int MAX_PATH_WEIGHT = Integer.MAX_VALUE - MAX_LINK_WEIGHT - 1; @@ -61,7 +60,7 @@ public class TopologyInstance { * switch ports may be provided at the time of instantiation. In addition, * we may add additional ports to this set. */ - protected Set<NodePortTuple> blockedPorts; + protected Set<NodePortTuple> blockedPorts; protected Map<NodePortTuple, Set<Link>> switchPortLinks; // Set of links organized by node port tuple /** Set of links that are blocked. */ protected Set<Link> blockedLinks; @@ -77,7 +76,23 @@ public class TopologyInstance { protected Map<Long, BroadcastTree> destinationRootedTrees; protected Map<Long, Set<NodePortTuple>> clusterBroadcastNodePorts; protected Map<Long, BroadcastTree> clusterBroadcastTrees; - protected LRUHashMap<RouteId, Route> pathcache; + + protected class PathCacheLoader extends CacheLoader<RouteId, Route> { + TopologyInstance ti; + PathCacheLoader(TopologyInstance ti) { + this.ti = ti; + } + + @Override + public Route load(RouteId rid) { + return ti.buildroute(rid); + } + } + + // Path cache loader is defined for loading a path when it not present + // in the cache. + private final PathCacheLoader pathCacheLoader = new PathCacheLoader(this); + protected LoadingCache<RouteId, Route> pathcache; public TopologyInstance() { this.switches = new HashSet<Long>(); @@ -95,7 +110,7 @@ public class TopologyInstance { { this.switches = new HashSet<Long>(switchPorts.keySet()); this.switchPorts = new HashMap<Long, Set<Short>>(switchPorts); - this.switchPortLinks = new HashMap<NodePortTuple, + this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(switchPortLinks); this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts); this.tunnelPorts = new HashSet<NodePortTuple>(); @@ -121,7 +136,7 @@ public class TopologyInstance { this.blockedPorts = new HashSet<NodePortTuple>(blockedPorts); this.switchPortLinks = new HashMap<NodePortTuple, Set<Link>>(); for(NodePortTuple npt: switchPortLinks.keySet()) { - this.switchPortLinks.put(npt, + this.switchPortLinks.put(npt, new HashSet<Link>(switchPortLinks.get(npt))); } this.broadcastDomainPorts = new HashSet<NodePortTuple>(broadcastDomainPorts); @@ -133,7 +148,15 @@ public class TopologyInstance { destinationRootedTrees = new HashMap<Long, BroadcastTree>(); clusterBroadcastTrees = new HashMap<Long, BroadcastTree>(); clusterBroadcastNodePorts = new HashMap<Long, Set<NodePortTuple>>(); - pathcache = new LRUHashMap<RouteId, Route>(PATH_CACHE_SIZE); + + pathcache = CacheBuilder.newBuilder().concurrencyLevel(4) + .maximumSize(1000L) + .build( + new CacheLoader<RouteId, Route>() { + public Route load(RouteId rid) { + return pathCacheLoader.load(rid); + } + }); } public void compute() { @@ -143,23 +166,19 @@ public class TopologyInstance { // Must ignore blocked links. identifyOpenflowDomains(); - // Step 0: Remove all links connected to blocked ports. - // removeLinksOnBlockedPorts(); - - // Step 1.1: Add links to clusters // Avoid adding blocked links to clusters addLinksToOpenflowDomains(); - // Step 2. Compute shortest path trees in each cluster for + // Step 2. Compute shortest path trees in each cluster for // unicast routing. The trees are rooted at the destination. // Cost for tunnel links and direct links are the same. calculateShortestPathTreeInClusters(); // Step 3. Compute broadcast tree in each cluster. - // Cost for tunnel links are high to discourage use of + // Cost for tunnel links are high to discourage use of // tunnel links. The cost is set to the number of nodes - // in the cluster + 1, to use as minimum number of + // in the cluster + 1, to use as minimum number of // clusters as possible. calculateBroadcastNodePortsInClusters(); @@ -268,7 +287,6 @@ public class TopologyInstance { * @param currSet: Set of nodes in the current cluster in formation * @return long: DSF index to be used when a new node is visited */ - private long dfsTraverse (long parentIndex, long currIndex, long currSw, Map<Long, ClusterDFS> dfsList, Set <Long> currSet) { @@ -296,7 +314,7 @@ public class TopologyInstance { // ignore incoming links. if (dstSw == currSw) continue; - // ignore if the destination is already added to + // ignore if the destination is already added to // another cluster if (switchClusterMap.get(dstSw) != null) continue; @@ -359,41 +377,6 @@ public class TopologyInstance { return currIndex; } - /** - * Go through every link and identify it is a blocked link or not. - * If blocked, remove it from the switchport links and put them in the - * blocked link category. - * - * Note that we do not update the tunnel ports and broadcast domain - * port structures. We need those to still answer the question if the - * ports are tunnel or broadcast domain ports. - * - * If we add additional ports to blocked ports later on, we may simply - * call this method again to remove the links on the newly blocked ports. - */ - protected void removeLinksOnBlockedPorts() { - Iterator<NodePortTuple> nptIter; - Iterator<Link> linkIter; - - // Iterate through all the links and all the switch ports - // and move the links on blocked switch ports to blocked links - nptIter = this.switchPortLinks.keySet().iterator(); - while (nptIter.hasNext()) { - NodePortTuple npt = nptIter.next(); - linkIter = switchPortLinks.get(npt).iterator(); - while (linkIter.hasNext()) { - Link link = linkIter.next(); - if (isBlockedLink(link)) { - this.blockedLinks.add(link); - linkIter.remove(); - } - } - // Note that at this point, the switchport may have - // no links in it. We could delete the switch port, - // but we will leave it as is. - } - } - public Set<NodePortTuple> getBlockedPorts() { return this.blockedPorts; } @@ -402,7 +385,7 @@ public class TopologyInstance { return this.blockedLinks; } - /** Returns true if a link has either one of its switch ports + /** Returns true if a link has either one of its switch ports * blocked. * @param l * @return @@ -438,12 +421,12 @@ public class TopologyInstance { } protected class NodeDist implements Comparable<NodeDist> { - private Long node; + private final Long node; public Long getNode() { return node; } - private int dist; + private final int dist; public int getDist() { return dist; } @@ -453,6 +436,7 @@ public class TopologyInstance { this.dist = dist; } + @Override public int compareTo(NodeDist o) { if (o.dist == this.dist) { return (int)(this.node - o.node); @@ -490,7 +474,7 @@ public class TopologyInstance { } } - protected BroadcastTree dijkstra(Cluster c, Long root, + protected BroadcastTree dijkstra(Cluster c, Long root, Map<Link, Integer> linkCost, boolean isDstRooted) { HashMap<Long, Link> nexthoplinks = new HashMap<Long, Link>(); @@ -551,7 +535,7 @@ public class TopologyInstance { } protected void calculateShortestPathTreeInClusters() { - pathcache.clear(); + pathcache.invalidateAll(); destinationRootedTrees.clear(); Map<Link, Integer> linkCost = new HashMap<Link, Integer>(); @@ -607,8 +591,10 @@ public class TopologyInstance { } } - protected Route buildroute(RouteId id, long srcId, long dstId) { + protected Route buildroute(RouteId id) { NodePortTuple npt; + long srcId = id.getSrc(); + long dstId = id.getDst(); LinkedList<NodePortTuple> switchPorts = new LinkedList<NodePortTuple>(); @@ -642,7 +628,7 @@ public class TopologyInstance { // else, no path exists, and path equals null Route result = null; - if (switchPorts != null && !switchPorts.isEmpty()) + if (switchPorts != null && !switchPorts.isEmpty()) result = new Route(id, switchPorts); if (log.isTraceEnabled()) { log.trace("buildroute: {}", result); @@ -656,7 +642,7 @@ public class TopologyInstance { return (bt.getCost(srcId)); } - /* + /* * Getter Functions */ @@ -702,15 +688,23 @@ public class TopologyInstance { return r; } + // NOTE: Return a null route if srcId equals dstId. The null route + // need not be stored in the cache. Moreover, the LoadingCache will + // throw an exception if null route is returned. protected Route getRoute(long srcId, long dstId, long cookie) { + // Return null route if srcId equals dstId + if (srcId == dstId) return null; + + RouteId id = new RouteId(srcId, dstId); Route result = null; - if (pathcache.containsKey(id)) { + + try { result = pathcache.get(id); - } else { - result = buildroute(id, srcId, dstId); - pathcache.put(id, result); + } catch (Exception e) { + log.error("{}", e); } + if (log.isTraceEnabled()) { log.trace("getRoute: {} -> {}", id, result); } @@ -723,9 +717,9 @@ public class TopologyInstance { return clusterBroadcastTrees.get(c.id); } - // + // // ITopologyService interface method helpers. - // + // protected boolean isInternalToOpenflowDomain(long switchid, short port) { return !isAttachmentPointPort(switchid, port); @@ -811,7 +805,7 @@ public class TopologyInstance { public NodePortTuple getIncomingSwitchPort(long src, short srcPort, long dst, short dstPort) { - // Use this function to reinject traffic from a + // Use this function to reinject traffic from a // different port if needed. return new NodePortTuple(src, srcPort); } diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index ed85b57acd205086ce948641d6b53cf09b8ae596..9bc0f030f89e5f94aa3bf56844683f58f102a912 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -68,21 +68,21 @@ import org.slf4j.LoggerFactory; /** * Topology manager is responsible for maintaining the controller's notion - * of the network graph, as well as implementing tools for finding routes + * of the network graph, as well as implementing tools for finding routes * through the topology. */ @LogMessageCategory("Network Topology") -public class TopologyManager implements - IFloodlightModule, ITopologyService, +public class TopologyManager implements + IFloodlightModule, ITopologyService, IRoutingService, ILinkDiscoveryListener, IOFMessageListener, IHAListener { protected static Logger log = LoggerFactory.getLogger(TopologyManager.class); - public static final String CONTEXT_TUNNEL_ENABLED = + public static final String CONTEXT_TUNNEL_ENABLED = "com.bigswitch.floodlight.topologymanager.tunnelEnabled"; - /** + /** * Set of ports for each switch */ protected Map<Long, Set<Short>> switchPorts; @@ -171,7 +171,7 @@ public class TopologyManager implements "discovery module.", recommendation=LogMessageDoc.CHECK_CONTROLLER) protected class UpdateTopologyWorker implements Runnable { - @Override + @Override public void run() { try { if (ldUpdates.peek() != null) @@ -223,7 +223,7 @@ public class TopologyManager implements } ldUpdates.add(update); } - + // **************** // ITopologyService // **************** @@ -241,16 +241,16 @@ public class TopologyManager implements topologyAware.add(listener); } - @Override + @Override public boolean isAttachmentPointPort(long switchid, short port) { return isAttachmentPointPort(switchid, port, true); } @Override - public boolean isAttachmentPointPort(long switchid, short port, + public boolean isAttachmentPointPort(long switchid, short port, boolean tunnelEnabled) { - // If the switch port is a tunnel endpoint, it is not + // If the switch port is 'tun-bsn' port, it is not // an attachment point port, irrespective of whether // a link is found through it or not. if (linkDiscovery.isTunnelPort(switchid, port)) @@ -273,10 +273,12 @@ public class TopologyManager implements return (sw.portEnabled(port)); } + @Override public long getOpenflowDomainId(long switchId) { return getOpenflowDomainId(switchId, true); } + @Override public long getOpenflowDomainId(long switchId, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getOpenflowDomainId(switchId); @@ -323,6 +325,7 @@ public class TopologyManager implements return isIncomingBroadcastAllowed(sw, portId, true); } + @Override public boolean isIncomingBroadcastAllowed(long sw, short portId, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); @@ -346,20 +349,22 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// - /** Get all the ports on the target switch (targetSw) on which a + /** Get all the ports on the target switch (targetSw) on which a * broadcast packet must be sent from a host whose attachment point * is on switch port (src, srcPort). */ - public Set<Short> getBroadcastPorts(long targetSw, + @Override + public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort) { return getBroadcastPorts(targetSw, src, srcPort, true); } - /** Get all the ports on the target switch (targetSw) on which a + /** Get all the ports on the target switch (targetSw) on which a * broadcast packet must be sent from a host whose attachment point * is on switch port (src, srcPort). */ - public Set<Short> getBroadcastPorts(long targetSw, + @Override + public Set<Short> getBroadcastPorts(long targetSw, long src, short srcPort, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); @@ -374,7 +379,7 @@ public class TopologyManager implements // Use this function to redirect traffic if needed. return getOutgoingSwitchPort(src, srcPort, dst, dstPort, true); } - + @Override public NodePortTuple getOutgoingSwitchPort(long src, short srcPort, long dst, short dstPort, @@ -502,13 +507,13 @@ public class TopologyManager implements //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// @Override - public NodePortTuple + public NodePortTuple getAllowedIncomingBroadcastPort(long src, short srcPort) { return getAllowedIncomingBroadcastPort(src,srcPort, true); } @Override - public NodePortTuple + public NodePortTuple getAllowedIncomingBroadcastPort(long src, short srcPort, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); @@ -541,6 +546,7 @@ public class TopologyManager implements return tunnelPorts; } + @Override public Set<NodePortTuple> getBlockedPorts() { Set<NodePortTuple> bp; Set<NodePortTuple> blockedPorts = @@ -587,12 +593,12 @@ public class TopologyManager implements } @Override - public Route getRoute(long src, short srcPort, long dst, short dstPort, long cookie, + public Route getRoute(long src, short srcPort, long dst, short dstPort, long cookie, boolean tunnelEnabled) { TopologyInstance ti = getCurrentInstance(tunnelEnabled); return ti.getRoute(src, srcPort, dst, dstPort, cookie); } - + @Override public boolean routeExists(long src, long dst) { return routeExists(src, dst, true); @@ -604,6 +610,16 @@ public class TopologyManager implements return ti.routeExists(src, dst); } + @Override + public ArrayList<Route> getRoutes(long srcDpid, long dstDpid, + boolean tunnelEnabled) { + // Floodlight supports single path routing now + + // return single path now + ArrayList<Route> result=new ArrayList<Route>(); + result.add(getRoute(srcDpid, dstDpid, 0, tunnelEnabled)); + return result; + } // ****************** // IOFMessageListener @@ -629,10 +645,10 @@ public class TopologyManager implements FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: - return this.processPacketInMessage(sw, + return this.processPacketInMessage(sw, (OFPacketIn) msg, cntx); default: - break; + break; } return Command.CONTINUE; @@ -678,7 +694,7 @@ public class TopologyManager implements @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { - Collection<Class<? extends IFloodlightService>> l = + Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(ITopologyService.class); l.add(IRoutingService.class); @@ -689,7 +705,7 @@ public class TopologyManager implements public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() { Map<Class<? extends IFloodlightService>, - IFloodlightService> m = + IFloodlightService> m = new HashMap<Class<? extends IFloodlightService>, IFloodlightService>(); // We are the class that implements the service @@ -699,9 +715,9 @@ public class TopologyManager implements } @Override - public Collection<Class<? extends IFloodlightService>> + public Collection<Class<? extends IFloodlightService>> getModuleDependencies() { - Collection<Class<? extends IFloodlightService>> l = + Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(ILinkDiscoveryService.class); l.add(IThreadPoolService.class); @@ -716,7 +732,7 @@ public class TopologyManager implements throws FloodlightModuleException { linkDiscovery = context.getServiceImpl(ILinkDiscoveryService.class); threadPool = context.getServiceImpl(IThreadPoolService.class); - floodlightProvider = + floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); restApi = context.getServiceImpl(IRestApiService.class); @@ -776,13 +792,10 @@ public class TopologyManager implements result = Command.STOP; } } - - // if sufficient information is available, then drop broadcast - // packets here as well. return result; } - /** + /** * TODO This method must be moved to a layer below forwarding * so that anyone can use it. * @param packetData @@ -795,14 +808,14 @@ public class TopologyManager implements explanation="An I/O error occured while trying send " + "topology discovery packet", recommendation=LogMessageDoc.CHECK_SWITCH) - public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw, + public void doMultiActionPacketOut(byte[] packetData, IOFSwitch sw, Set<Short> ports, FloodlightContext cntx) { if (ports == null) return; if (packetData == null || packetData.length <= 0) return; - OFPacketOut po = + OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory(). getMessage(OFType.PACKET_OUT); @@ -814,7 +827,7 @@ public class TopologyManager implements // set actions po.setActions(actions); // set action length - po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * + po.setActionsLength((short) (OFActionOutput.MINIMUM_LENGTH * ports.size())); // set buffer-id to BUFFER_ID_NONE po.setBufferId(OFPacketOut.BUFFER_ID_NONE); @@ -825,8 +838,8 @@ public class TopologyManager implements po.setPacketData(packetData); // compute and set packet length. - short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + - po.getActionsLength() + + short poLength = (short)(OFPacketOut.MINIMUM_LENGTH + + po.getActionsLength() + packetData.length); po.setLength(poLength); @@ -834,7 +847,7 @@ public class TopologyManager implements try { //counterStore.updatePktOutFMCounterStore(sw, po); if (log.isTraceEnabled()) { - log.trace("write broadcast packet on switch-id={} " + + log.trace("write broadcast packet on switch-id={} " + "interaces={} packet-data={} packet-out={}", new Object[] {sw.getId(), ports, packetData, po}); } @@ -870,13 +883,13 @@ public class TopologyManager implements /** * The BDDP packets are forwarded out of all the ports out of an * openflowdomain. Get all the switches in the same openflow - * domain as the sw (disabling tunnels). Then get all the + * domain as the sw (disabling tunnels). Then get all the * external switch ports and send these packets out. * @param sw * @param pi * @param cntx */ - protected void doFloodBDDP(long pinSwitch, OFPacketIn pi, + protected void doFloodBDDP(long pinSwitch, OFPacketIn pi, FloodlightContext cntx) { TopologyInstance ti = getCurrentInstance(false); @@ -900,13 +913,13 @@ public class TopologyManager implements ports.addAll(enabledPorts); // all the ports known to topology // without tunnels. - // out of these, we need to choose only those that are + // out of these, we need to choose only those that are // broadcast port, otherwise, we should eliminate. Set<Short> portsKnownToTopo = ti.getPortsWithLinks(sid); if (portsKnownToTopo != null) { for(short p: portsKnownToTopo) { - NodePortTuple npt = + NodePortTuple npt = new NodePortTuple(sid, p); if (ti.isBroadcastDomainPort(npt) == false) { ports.remove(p); @@ -930,11 +943,11 @@ public class TopologyManager implements } - protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, + protected Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { // get the packet-in switch. - Ethernet eth = + Ethernet eth = IFloodlightProviderService.bcStore. get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); @@ -949,13 +962,12 @@ public class TopologyManager implements return Command.CONTINUE; doFloodBDDP(sw.getId(), pi, cntx); + return Command.STOP; } else { return dropFilter(sw.getId(), pi, cntx); } - return Command.STOP; } - /** * Updates concerning switch disconnect and port down are not processed. * LinkDiscoveryManager is expected to process those messages and send @@ -983,7 +995,7 @@ public class TopologyManager implements update.getDst(), update.getDstPort(), update.getType()); } else if (update.getOperation() == UpdateOperation.LINK_REMOVED){ - removeLink(update.getSrc(), update.getSrcPort(), + removeLink(update.getSrc(), update.getSrcPort(), update.getDst(), update.getDstPort()); } else if (update.getOperation() == UpdateOperation.TUNNEL_PORT_ADDED) { addTunnelPort(update.getSrc(), update.getSrcPort()); @@ -1007,9 +1019,6 @@ public class TopologyManager implements tunnelPortsUpdated = true; } - /** - * This function computes a new topology. - */ /** * This function computes a new topology instance. * It ignores links connected to all broadcast domain ports @@ -1022,7 +1031,7 @@ public class TopologyManager implements if (!linksUpdated) return false; Map<NodePortTuple, Set<Link>> openflowLinks; - openflowLinks = + openflowLinks = new HashMap<NodePortTuple, Set<Link>>(); Set<NodePortTuple> nptList = switchPortLinks.keySet(); @@ -1056,9 +1065,9 @@ public class TopologyManager implements } } - TopologyInstance nt = new TopologyInstance(switchPorts, + TopologyInstance nt = new TopologyInstance(switchPorts, blockedPorts, - openflowLinks, + openflowLinks, broadcastDomainPorts, tunnelPorts); nt.compute(); @@ -1162,7 +1171,7 @@ public class TopologyManager implements } public boolean removeSwitch(long sid) { - // Delete all the links in the switch, switch and all + // Delete all the links in the switch, switch and all // associated data should be deleted. if (switchPorts.containsKey(sid) == false) return false; @@ -1187,9 +1196,9 @@ public class TopologyManager implements * @param l * @return */ - private boolean addLinkToStructure(Map<NodePortTuple, + private boolean addLinkToStructure(Map<NodePortTuple, Set<Link>> s, Link l) { - boolean result1 = false, result2 = false; + boolean result1 = false, result2 = false; NodePortTuple n1 = new NodePortTuple(l.getSrc(), l.getSrcPort()); NodePortTuple n2 = new NodePortTuple(l.getDst(), l.getDstPort()); @@ -1213,7 +1222,7 @@ public class TopologyManager implements * @param l * @return */ - private boolean removeLinkFromStructure(Map<NodePortTuple, + private boolean removeLinkFromStructure(Map<NodePortTuple, Set<Link>> s, Link l) { boolean result1 = false, result2 = false; @@ -1236,7 +1245,7 @@ public class TopologyManager implements // If you need to handle tunnel links, this is a placeholder. } - public void addOrUpdateLink(long srcId, short srcPort, long dstId, + public void addOrUpdateLink(long srcId, short srcPort, long dstId, short dstPort, LinkType type) { Link link = new Link(srcId, srcPort, dstId, dstPort); @@ -1268,9 +1277,9 @@ public class TopologyManager implements removeLinkFromStructure(portBroadcastDomainLinks, link); removeLinkFromStructure(switchPortLinks, link); - NodePortTuple srcNpt = + NodePortTuple srcNpt = new NodePortTuple(link.getSrc(), link.getSrcPort()); - NodePortTuple dstNpt = + NodePortTuple dstNpt = new NodePortTuple(link.getDst(), link.getDstPort()); // Remove switch ports if there are no links through those switch ports @@ -1284,11 +1293,11 @@ public class TopologyManager implements } // Remove the node if no ports are present - if (switchPorts.get(srcNpt.getNodeId())!=null && + if (switchPorts.get(srcNpt.getNodeId())!=null && switchPorts.get(srcNpt.getNodeId()).isEmpty()) { switchPorts.remove(srcNpt.getNodeId()); } - if (switchPorts.get(dstNpt.getNodeId())!=null && + if (switchPorts.get(dstNpt.getNodeId())!=null && switchPorts.get(dstNpt.getNodeId()).isEmpty()) { switchPorts.remove(dstNpt.getNodeId()); } @@ -1350,6 +1359,7 @@ public class TopologyManager implements /** * Switch methods */ + @Override public Set<Short> getPorts(long sw) { Set<Short> ports = new HashSet<Short>(); IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); @@ -1366,14 +1376,5 @@ public class TopologyManager implements return ports; } - @Override - public ArrayList<Route> getRoutes(long srcDpid, long dstDpid, - boolean tunnelEnabled) { - // Floodlight supports single path routing now - - // return single path now - ArrayList<Route> result=new ArrayList<Route>(); - result.add(getRoute(srcDpid, dstDpid, 0, tunnelEnabled)); - return result; - } + } diff --git a/src/main/java/org/openflow/protocol/OFFlowRemoved.java b/src/main/java/org/openflow/protocol/OFFlowRemoved.java index 4f4e0c7ff92acbd9a716c5301bdcd93a06335ae4..33570063c73850cf995606890ac0c3e9e97f67f5 100644 --- a/src/main/java/org/openflow/protocol/OFFlowRemoved.java +++ b/src/main/java/org/openflow/protocol/OFFlowRemoved.java @@ -194,7 +194,11 @@ public class OFFlowRemoved extends OFMessage { this.match.readFrom(data); this.cookie = data.readLong(); this.priority = data.readShort(); - this.reason = OFFlowRemovedReason.values()[(0xff & data.readByte())]; + int reasonIndex = (int)(0xff & data.readByte()); + if (reasonIndex >= OFFlowRemovedReason.values().length) { + reasonIndex = OFFlowRemovedReason.values().length - 1; + } + this.reason = OFFlowRemovedReason.values()[reasonIndex]; data.readByte(); // pad this.durationSeconds = data.readInt(); this.durationNanoseconds = data.readInt(); diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index f6f94f880fda7904abd0cc68580094c9713f4971..fd373279e85f151de765bd4c5cfc7af5de3fac68 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.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 @@ -71,6 +71,7 @@ import org.easymock.EasyMock; import org.jboss.netty.channel.Channel; import org.junit.Test; import org.openflow.protocol.OFError; +import org.openflow.protocol.OFError.OFBadRequestCode; import org.openflow.protocol.OFError.OFErrorType; import org.openflow.protocol.OFFeaturesReply; import org.openflow.protocol.OFMessage; @@ -101,7 +102,7 @@ import org.openflow.vendor.nicira.OFRoleReplyVendorData; */ public class ControllerTest extends FloodlightTestCase implements IOFSwitchDriver { - + private Controller controller; private MockThreadPoolService tp; private boolean test_bind_order = false; @@ -111,26 +112,26 @@ public class ControllerTest extends FloodlightTestCase public void setUp() throws Exception { super.setUp(); FloodlightModuleContext fmc = new FloodlightModuleContext(); - + FloodlightProvider cm = new FloodlightProvider(); controller = (Controller)cm.getServiceImpls().get(IFloodlightProviderService.class); fmc.addService(IFloodlightProviderService.class, controller); - + MemoryStorageSource memstorage = new MemoryStorageSource(); fmc.addService(IStorageSourceService.class, memstorage); - + RestApiServer restApi = new RestApiServer(); fmc.addService(IRestApiService.class, restApi); - + CounterStore cs = new CounterStore(); fmc.addService(ICounterStoreService.class, cs); - + PktInProcessingTime ppt = new PktInProcessingTime(); fmc.addService(IPktInProcessingTimeService.class, ppt); - + tp = new MockThreadPoolService(); fmc.addService(IThreadPoolService.class, tp); - + ppt.init(fmc); restApi.init(fmc); memstorage.init(fmc); @@ -161,15 +162,15 @@ public class ControllerTest extends FloodlightTestCase sr.setFlags((short) 1); return sr; } - + /* Set the mock expectations for sw when sw is passed to addSwitch */ protected void setupSwitchForAddSwitch(IOFSwitch sw, long dpid) { String dpidString = HexString.toHexString(dpid); - + expect(sw.getId()).andReturn(dpid).anyTimes(); expect(sw.getStringId()).andReturn(dpidString).anyTimes(); } - + /** * Run the controller's main loop so that updates are processed */ @@ -213,7 +214,7 @@ public class ControllerTest extends FloodlightTestCase .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - // Build the PacketIn + // Build the PacketIn OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) @@ -244,7 +245,7 @@ public class ControllerTest extends FloodlightTestCase // verify STOP works reset(test1, test2, sw); - expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class))).andReturn(Command.STOP); + expect(test1.receive(eq(sw), eq(pi), isA(FloodlightContext.class))).andReturn(Command.STOP); expect(sw.getId()).andReturn(0L).anyTimes(); expect(sw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes(); replay(test1, test2, sw); @@ -285,7 +286,7 @@ public class ControllerTest extends FloodlightTestCase } /** - * + * * @throws Exception */ @Test @@ -373,7 +374,7 @@ public class ControllerTest extends FloodlightTestCase fmCntx.addService(IFloodlightProviderService.class, mfp); fmCntx.addService(IThreadPoolService.class, mtp); String sid = null; - + mfm.init(fmCntx); mfm.startUp(fmCntx); @@ -396,14 +397,14 @@ public class ControllerTest extends FloodlightTestCase assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize()); // Deleting the filter works. - mfm.setupFilter(sid, null, -1); + mfm.setupFilter(sid, null, -1); assertTrue(mfm.getNumberOfFilters() == mfm.getMaxFilterSize()-1); - // Creating mock switch to which we will send packet out and + // Creating mock switch to which we will send packet out and IOFSwitch sw = createMock(IOFSwitch.class); expect(sw.getId()).andReturn(new Long(0)); - // Mock Packet-in + // Mock Packet-in IPacket testPacket = new Ethernet() .setSourceMACAddress("00:44:33:22:11:00") .setDestinationMACAddress("00:11:22:33:44:55") @@ -421,7 +422,7 @@ public class ControllerTest extends FloodlightTestCase .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - // Build the PacketIn + // Build the PacketIn OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) @@ -446,7 +447,7 @@ public class ControllerTest extends FloodlightTestCase // Let's check the listeners. - List <IOFMessageListener> lm; + List <IOFMessageListener> lm; // Check to see if all the listeners are active. lm = mfp.getListeners().get(OFType.PACKET_OUT); @@ -461,7 +462,7 @@ public class ControllerTest extends FloodlightTestCase assertTrue(lm.size() == 1); assertTrue(lm.get(0).equals(mfm)); - HashSet<String> matchedFilters; + HashSet<String> matchedFilters; // Send a packet in and check if it matches a filter. matchedFilters = mfm.getMatchedFilters(pi, cntx); @@ -495,14 +496,14 @@ public class ControllerTest extends FloodlightTestCase expect(newsw.getId()).andReturn(0L).anyTimes(); expect(newsw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes(); controller.activeSwitches.put(0L, oldsw); - + replay(newsw, channel); controller.addSwitch(newsw, false); verify(newsw, channel); } - + @Test public void testAddSwitchClearFM() throws Exception { controller.activeSwitches = new ConcurrentHashMap<Long, IOFSwitch>(); @@ -524,14 +525,14 @@ public class ControllerTest extends FloodlightTestCase newsw.clearAllFlowMods(); expectLastCall().once(); controller.activeSwitches.put(0L, oldsw); - + replay(newsw, channel); controller.addSwitch(newsw, true); verify(newsw, channel); } - + @Test public void testUpdateQueue() throws Exception { class DummySwitchListener implements IOFSwitchListener { @@ -567,29 +568,29 @@ public class ControllerTest extends FloodlightTestCase IOFSwitch sw = createMock(IOFSwitch.class); ControllerRunThread t = new ControllerRunThread(); t.start(); - + controller.addOFSwitchListener(switchListener); synchronized(switchListener) { controller.updates.put(controller.new SwitchUpdate(sw, Controller.SwitchUpdateType.ADDED)); switchListener.wait(500); - assertTrue("IOFSwitchListener.addedSwitch() was not called", + assertTrue("IOFSwitchListener.addedSwitch() was not called", switchListener.nAdded == 1); - controller.updates.put(controller.new SwitchUpdate(sw, + controller.updates.put(controller.new SwitchUpdate(sw, Controller.SwitchUpdateType.REMOVED)); switchListener.wait(500); - assertTrue("IOFSwitchListener.removedSwitch() was not called", + assertTrue("IOFSwitchListener.removedSwitch() was not called", switchListener.nRemoved == 1); - controller.updates.put(controller.new SwitchUpdate(sw, + controller.updates.put(controller.new SwitchUpdate(sw, Controller.SwitchUpdateType.PORTCHANGED)); switchListener.wait(500); - assertTrue("IOFSwitchListener.switchPortChanged() was not called", + assertTrue("IOFSwitchListener.switchPortChanged() was not called", switchListener.nPortChanged == 1); } } - - private Map<String,Object> getFakeControllerIPRow(String id, String controllerId, + + private Map<String,Object> getFakeControllerIPRow(String id, String controllerId, String type, int number, String discoveredIP ) { HashMap<String, Object> row = new HashMap<String,Object>(); row.put(Controller.CONTROLLER_INTERFACE_ID, id); @@ -602,14 +603,14 @@ public class ControllerTest extends FloodlightTestCase /** * Test notifications for controller node IP changes. This requires - * synchronization between the main test thread and another thread + * synchronization between the main test thread and another thread * that runs Controller's main loop and takes / handles updates. We - * synchronize with wait(timeout) / notifyAll(). We check for the + * synchronize with wait(timeout) / notifyAll(). We check for the * expected condition after the wait returns. However, if wait returns * due to the timeout (or due to spurious awaking) and the check fails we * might just not have waited long enough. Using a long enough timeout - * mitigates this but we cannot get rid of the fundamental "issue". - * + * mitigates this but we cannot get rid of the fundamental "issue". + * * @throws Exception */ @Test @@ -619,16 +620,16 @@ public class ControllerTest extends FloodlightTestCase public Map<String, String> addedControllerNodeIPs; public Map<String, String> removedControllerNodeIPs; public int nCalled; - + public DummyHAListener() { this.nCalled = 0; } - + @Override public void roleChanged(Role oldRole, Role newRole) { // ignore } - + @Override public synchronized void controllerNodeIPsChanged( Map<String, String> curControllerNodeIPs, @@ -640,19 +641,19 @@ public class ControllerTest extends FloodlightTestCase this.nCalled++; notifyAll(); } - + public void do_assert(int nCalled, Map<String, String> curControllerNodeIPs, Map<String, String> addedControllerNodeIPs, Map<String, String> removedControllerNodeIPs) { assertEquals("nCalled is not as expected", nCalled, this.nCalled); - assertEquals("curControllerNodeIPs is not as expected", + assertEquals("curControllerNodeIPs is not as expected", curControllerNodeIPs, this.curControllerNodeIPs); - assertEquals("addedControllerNodeIPs is not as expected", + assertEquals("addedControllerNodeIPs is not as expected", addedControllerNodeIPs, this.addedControllerNodeIPs); - assertEquals("removedControllerNodeIPs is not as expected", + assertEquals("removedControllerNodeIPs is not as expected", removedControllerNodeIPs, this.removedControllerNodeIPs); - + } } long waitTimeout = 250; // ms @@ -660,11 +661,11 @@ public class ControllerTest extends FloodlightTestCase HashMap<String,String> expectedCurMap = new HashMap<String, String>(); HashMap<String,String> expectedAddedMap = new HashMap<String, String>(); HashMap<String,String> expectedRemovedMap = new HashMap<String, String>(); - + controller.addHAListener(listener); ControllerRunThread t = new ControllerRunThread(); t.start(); - + synchronized(listener) { // Insert a first entry controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, @@ -676,12 +677,12 @@ public class ControllerTest extends FloodlightTestCase expectedAddedMap.put("c1", "1.1.1.1"); listener.wait(waitTimeout); listener.do_assert(1, expectedCurMap, expectedAddedMap, expectedRemovedMap); - - // Add an interface that we want to ignore. + + // Add an interface that we want to ignore. controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row2", "c1", "Ethernet", 1, "1.1.1.2")); listener.wait(waitTimeout); // TODO: do a different check. This call will have to wait for the timeout - assertTrue("controllerNodeIPsChanged() should not have been called here", + assertTrue("controllerNodeIPsChanged() should not have been called here", listener.nCalled == 1); // Add another entry @@ -711,7 +712,7 @@ public class ControllerTest extends FloodlightTestCase listener.do_assert(3, expectedCurMap, expectedAddedMap, expectedRemovedMap); // Delete an entry - controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, + controller.storageSource.deleteRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, "row3"); expectedCurMap.clear(); expectedAddedMap.clear(); @@ -722,11 +723,11 @@ public class ControllerTest extends FloodlightTestCase listener.do_assert(4, expectedCurMap, expectedAddedMap, expectedRemovedMap); } } - + @Test public void testGetControllerNodeIPs() { HashMap<String,String> expectedCurMap = new HashMap<String, String>(); - + controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row1", "c1", "Ethernet", 0, "1.1.1.1")); controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, @@ -734,46 +735,46 @@ public class ControllerTest extends FloodlightTestCase controller.storageSource.insertRow(Controller.CONTROLLER_INTERFACE_TABLE_NAME, getFakeControllerIPRow("row3", "c2", "Ethernet", 0, "2.2.2.2")); expectedCurMap.put("c1", "1.1.1.1"); - expectedCurMap.put("c2", "2.2.2.2"); - assertEquals("expectedControllerNodeIPs is not as expected", + expectedCurMap.put("c2", "2.2.2.2"); + assertEquals("expectedControllerNodeIPs is not as expected", expectedCurMap, controller.getControllerNodeIPs()); } - + @Test public void testSetRoleNull() { try { - controller.setRole(null); + controller.setRole(null, null); fail("Should have thrown an Exception"); } catch (NullPointerException e) { //exptected } } - - @Test + + @Test public void testSetRole() { controller.connectedSwitches.add(new OFSwitchImpl()); - RoleChanger roleChanger = createMock(RoleChanger.class); + RoleChanger roleChanger = createMock(RoleChanger.class); roleChanger.submitRequest(controller.connectedSwitches, Role.SLAVE); controller.roleChanger = roleChanger; - - assertEquals("Check that update queue is empty", 0, + + assertEquals("Check that update queue is empty", 0, controller.updates.size()); - + replay(roleChanger); - controller.setRole(Role.SLAVE); + controller.setRole(Role.SLAVE, "Testing"); controller.doSetRole(); // avoid wait verify(roleChanger); - + Controller.IUpdate upd = controller.updates.poll(); assertNotNull("Check that update queue has an update", upd); - assertTrue("Check that update is HARoleUpdate", + assertTrue("Check that update is HARoleUpdate", upd instanceof Controller.HARoleUpdate); Controller.HARoleUpdate roleUpd = (Controller.HARoleUpdate)upd; assertSame(Role.MASTER, roleUpd.oldRole); assertSame(Role.SLAVE, roleUpd.newRole); } - + @SuppressWarnings("unchecked") @Test public void testCheckSwitchReady() { @@ -784,8 +785,8 @@ public class ControllerTest extends FloodlightTestCase OFDescriptionStatistics desc = new OFDescriptionStatistics(); OFFeaturesReply featuresReply = new OFFeaturesReply(); featuresReply.setPorts(new ArrayList<OFPhysicalPort>()); - - // Wrong current state + + // Wrong current state // Should not go to READY state.hsState = OFChannelState.HandshakeState.HELLO; state.hasDescription = false; @@ -796,7 +797,7 @@ public class ControllerTest extends FloodlightTestCase replay(channel); chdlr.checkSwitchReady(); assertSame(OFChannelState.HandshakeState.HELLO, state.hsState); - + // Have only config reply state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY; state.hasDescription = false; @@ -807,7 +808,7 @@ public class ControllerTest extends FloodlightTestCase assertSame(OFChannelState.HandshakeState.FEATURES_REPLY, state.hsState); assertTrue(controller.connectedSwitches.isEmpty()); assertTrue(controller.activeSwitches.isEmpty()); - + // Have only desc reply state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY; state.hasDescription = true; @@ -818,7 +819,7 @@ public class ControllerTest extends FloodlightTestCase assertSame(OFChannelState.HandshakeState.FEATURES_REPLY, state.hsState); assertTrue(controller.connectedSwitches.isEmpty()); assertTrue(controller.activeSwitches.isEmpty()); - + ////////////////////////////////////////// // Finally, everything is right. Should advance to READY ////////////////////////////////////////// @@ -830,7 +831,7 @@ public class ControllerTest extends FloodlightTestCase state.featuresReply = featuresReply; state.switchBindingDone = false; // Role support disabled. Switch should be promoted to active switch - // list. + // list. // setupSwitchForAddSwitch(chdlr.sw, 0L); // chdlr.sw.clearAllFlowMods(); desc.setManufacturerDescription("test vendor"); @@ -844,14 +845,14 @@ public class ControllerTest extends FloodlightTestCase reset(controller.roleChanger); controller.connectedSwitches.clear(); controller.activeSwitches.clear(); - - - // Role support enabled. + + + // Role support enabled. state.hsState = OFChannelState.HandshakeState.FEATURES_REPLY; controller.role = Role.MASTER; - Capture<Collection<IOFSwitch>> swListCapture = + Capture<Collection<IOFSwitch>> swListCapture = new Capture<Collection<IOFSwitch>>(); - controller.roleChanger.submitRequest(capture(swListCapture), + controller.roleChanger.submitRequest(capture(swListCapture), same(Role.MASTER)); replay(controller.roleChanger); chdlr.checkSwitchReady(); @@ -862,7 +863,7 @@ public class ControllerTest extends FloodlightTestCase Collection<IOFSwitch> swList = swListCapture.getValue(); assertEquals(1, swList.size()); } - + public class TestSwitchClass extends OFSwitchImpl { } @@ -872,11 +873,11 @@ public class ControllerTest extends FloodlightTestCase @Test public void testBindSwitchToDriver() { controller.addOFSwitchDriver("test", this); - + OFChannelState state = new OFChannelState(); Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); - + // Swith should be bound of OFSwitchImpl (default) state.hsState = OFChannelState.HandshakeState.HELLO; state.hasDescription = true; @@ -893,7 +894,7 @@ public class ControllerTest extends FloodlightTestCase chdlr.bindSwitchToDriver(); assertTrue(chdlr.sw instanceof OFSwitchImpl); assertTrue(!(chdlr.sw instanceof TestSwitchClass)); - + // Switch should be bound to TestSwitchImpl state.switchBindingDone = false; desc.setManufacturerDescription("test1 switch"); @@ -914,7 +915,7 @@ public class ControllerTest extends FloodlightTestCase chdlr.bindSwitchToDriver(); assertTrue(chdlr.sw instanceof Test11SwitchClass); } - + @Test public void testBindSwitchOrder() { List<String> order = new ArrayList<String>(3); @@ -925,12 +926,12 @@ public class ControllerTest extends FloodlightTestCase order.add("test"); order.add(""); test_bind_order = true; - + OFChannelState state = new OFChannelState(); Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = null; - + // Swith should be bound of OFSwitchImpl (default) state.hsState = OFChannelState.HandshakeState.HELLO; state.hasDescription = true; @@ -952,29 +953,29 @@ public class ControllerTest extends FloodlightTestCase test_bind_order = false; bind_order = null; } - + @Test public void testChannelDisconnected() throws Exception { OFChannelState state = new OFChannelState(); state.hsState = OFChannelState.HandshakeState.READY; Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = createMock(IOFSwitch.class); - - // Switch is active + + // Switch is active expect(chdlr.sw.getId()).andReturn(0L).anyTimes(); expect(chdlr.sw.getStringId()).andReturn("00:00:00:00:00:00:00:00") .anyTimes(); chdlr.sw.cancelAllStatisticsReplies(); chdlr.sw.setConnected(false); expect(chdlr.sw.isConnected()).andReturn(true); - + controller.connectedSwitches.add(chdlr.sw); controller.activeSwitches.put(0L, chdlr.sw); - + replay(chdlr.sw); chdlr.channelDisconnected(null, null); verify(chdlr.sw); - + // Switch is connected but not active reset(chdlr.sw); expect(chdlr.sw.getId()).andReturn(0L).anyTimes(); @@ -982,20 +983,20 @@ public class ControllerTest extends FloodlightTestCase replay(chdlr.sw); chdlr.channelDisconnected(null, null); verify(chdlr.sw); - + // Not in ready state state.hsState = HandshakeState.START; reset(chdlr.sw); replay(chdlr.sw); chdlr.channelDisconnected(null, null); verify(chdlr.sw); - + // Switch is null state.hsState = HandshakeState.READY; chdlr.sw = null; chdlr.channelDisconnected(null, null); } - + /* @Test public void testRoleChangeForSerialFailoverSwitch() throws Exception { @@ -1004,12 +1005,12 @@ public class ControllerTest extends FloodlightTestCase expect(newsw.getStringId()).andReturn("00:00:00:00:00:00:00").anyTimes(); Channel channel2 = createMock(Channel.class); expect(newsw.getChannel()).andReturn(channel2); - + // newsw.role is null because the switch does not support // role request messages expect(newsw.getAttribute(IOFSwitch.SWITCH_SUPPORTS_NX_ROLE)) .andReturn(false); - // switch is connected + // switch is connected controller.connectedSwitches.add(newsw); // the switch should get disconnected when role is changed to SLAVE @@ -1029,40 +1030,40 @@ public class ControllerTest extends FloodlightTestCase Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = createMock(IOFSwitch.class); Channel ch = createMock(Channel.class); - + // the error returned when role request message is not supported by sw OFError msg = new OFError(); msg.setType(OFType.ERROR); msg.setXid(xid); msg.setErrorType(OFErrorType.OFPET_BAD_REQUEST); - + // the switch connection should get disconnected when the controller is // in SLAVE mode and the switch does not support role-request messages controller.role = Role.SLAVE; - setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); + setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.SLAVE, false); expect(chdlr.sw.getHARole()).andReturn(Role.SLAVE); chdlr.sw.disconnectOutputStream(); - + replay(ch, chdlr.sw); chdlr.processOFMessage(msg); verify(ch, chdlr.sw); assertTrue("activeSwitches must be empty", controller.activeSwitches.isEmpty()); reset(ch, chdlr.sw); - + // We are MASTER, the switch should be added to the list of active // switches. controller.role = Role.MASTER; - setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); + setupPendingRoleRequest(chdlr.sw, xid, controller.role, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(controller.role, false); setupSwitchForAddSwitch(chdlr.sw, 0L); chdlr.sw.clearAllFlowMods(); expect(chdlr.sw.getHARole()).andReturn(null).anyTimes(); replay(ch, chdlr.sw); - + chdlr.processOFMessage(msg); verify(ch, chdlr.sw); assertSame("activeSwitches must contain this switch", @@ -1070,12 +1071,12 @@ public class ControllerTest extends FloodlightTestCase reset(ch, chdlr.sw); } - - - @Test + + + @Test public void testVendorMessageUnknown() throws Exception { // Check behavior with an unknown vendor id - // Ensure that vendor message listeners get called, even for Vendors + // Ensure that vendor message listeners get called, even for Vendors // unknown to floodlight. It is the responsibility of the listener to // discard unknown vendors. OFChannelState state = new OFChannelState(); @@ -1086,15 +1087,15 @@ public class ControllerTest extends FloodlightTestCase IOFSwitch sw = createMock(IOFSwitch.class); chdlr.sw = sw; controller.activeSwitches.put(1L, sw); - + // prepare the Vendor Message Listener expectations - ListenerDispatcher<OFType, IOFMessageListener> ld = + ListenerDispatcher<OFType, IOFMessageListener> ld = new ListenerDispatcher<OFType, IOFMessageListener>(); IOFMessageListener ml = createMock(IOFMessageListener.class); expect(ml.getName()).andReturn("Dummy").anyTimes(); - expect(ml.isCallbackOrderingPrereq((OFType)anyObject(), + expect(ml.isCallbackOrderingPrereq((OFType)anyObject(), (String)anyObject())).andReturn(false).anyTimes(); - expect(ml.isCallbackOrderingPostreq((OFType)anyObject(), + expect(ml.isCallbackOrderingPostreq((OFType)anyObject(), (String)anyObject())).andReturn(false).anyTimes(); expect(ml.receive(eq(sw), eq(msg), isA(FloodlightContext.class))). andReturn(Command.CONTINUE).once(); @@ -1106,14 +1107,47 @@ public class ControllerTest extends FloodlightTestCase expect(sw.isConnected()).andReturn(true).anyTimes(); expect(sw.getHARole()).andReturn(Role.MASTER).anyTimes(); expect(sw.getId()).andReturn(1L).anyTimes(); - + // test replay(chdlr.sw, lock, ml); ld.addListener(OFType.VENDOR, ml); chdlr.processOFMessage(msg); } - - + + @Test + public void testErrorEPERM() throws Exception { + // Check behavior with a BAD_REQUEST/EPERM error + // Ensure controller attempts to reset switch role. + OFChannelState state = new OFChannelState(); + state.hsState = HandshakeState.READY; + Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); + OFError error = new OFError(); + error.setErrorType(OFErrorType.OFPET_BAD_REQUEST); + error.setErrorCode(OFBadRequestCode.OFPBRC_EPERM); + IOFSwitch sw = createMock(IOFSwitch.class); + chdlr.sw = sw; + controller.activeSwitches.put(1L, sw); + + // prepare the switch and lock expectations + Lock lock = createNiceMock(Lock.class); + expect(sw.getListenerReadLock()).andReturn(lock).anyTimes(); + expect(sw.isConnected()).andReturn(true).anyTimes(); + expect(sw.getHARole()).andReturn(Role.MASTER).anyTimes(); + expect(sw.getId()).andReturn(1L).anyTimes(); + + // Make sure controller attempts to reset switch master + expect(sw.getAttribute("supportsNxRole")).andReturn(true).anyTimes(); + expect(sw.getNextTransactionId()).andReturn(0).anyTimes(); + sw.write(EasyMock.<List<OFMessage>> anyObject(), + (FloodlightContext)anyObject()); + + // test + replay(sw, lock); + chdlr.processOFMessage(error); + // Verify there is a pending role change request + assertTrue(controller.roleChanger.pendingTasks.peek() != null); + } + // Helper function. protected Controller.OFChannelHandler getChannelHandlerForRoleReplyTest() { OFChannelState state = new OFChannelState(); @@ -1122,19 +1156,19 @@ public class ControllerTest extends FloodlightTestCase chdlr.sw = createMock(IOFSwitch.class); return chdlr; } - + // Helper function protected OFVendor getRoleReplyMsgForRoleReplyTest(int xid, int nicira_role) { OFVendor msg = new OFVendor(); msg.setXid(xid); msg.setVendor(OFNiciraVendorData.NX_VENDOR_ID); - OFRoleReplyVendorData roleReplyVendorData = + OFRoleReplyVendorData roleReplyVendorData = new OFRoleReplyVendorData(OFRoleReplyVendorData.NXT_ROLE_REPLY); msg.setVendorData(roleReplyVendorData); roleReplyVendorData.setRole(nicira_role); return msg; } - + // Helper function protected void setupPendingRoleRequest(IOFSwitch sw, int xid, Role role, long cookie) { @@ -1145,10 +1179,10 @@ public class ControllerTest extends FloodlightTestCase new PendingRoleRequestEntry(xid, role, cookie); pendingList.add(entry); } - + /** invalid role in role reply */ - @Test - public void testNiciraRoleReplyInvalidRole() + @Test + public void testNiciraRoleReplyInvalidRole() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); @@ -1159,17 +1193,17 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(msg); verify(chdlr.sw, ch); } - + /** First role reply message received: transition from slave to master */ - @Test - public void testNiciraRoleReplySlave2MasterFristTime() + @Test + public void testNiciraRoleReplySlave2MasterFristTime() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_MASTER); - setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); + setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.MASTER, true); setupSwitchForAddSwitch(chdlr.sw, 1L); @@ -1180,18 +1214,18 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); } - - + + /** Not first role reply message received: transition from slave to master */ - @Test - public void testNiciraRoleReplySlave2MasterNotFristTime() + @Test + public void testNiciraRoleReplySlave2MasterNotFristTime() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_MASTER); - setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); + setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); expect(chdlr.sw.getHARole()).andReturn(Role.SLAVE); chdlr.sw.setHARole(Role.MASTER, true); setupSwitchForAddSwitch(chdlr.sw, 1L); @@ -1202,17 +1236,17 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); } - + /** transition from slave to equal */ - @Test - public void testNiciraRoleReplySlave2Equal() + @Test + public void testNiciraRoleReplySlave2Equal() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_OTHER); - - setupPendingRoleRequest(chdlr.sw, xid, Role.EQUAL, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.EQUAL, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.EQUAL, true); setupSwitchForAddSwitch(chdlr.sw, 1L); @@ -1223,16 +1257,16 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); }; - + @Test /** Slave2Slave transition ==> no change */ public void testNiciraRoleReplySlave2Slave() throws Exception{ int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); - OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, + OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_SLAVE); - - setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.SLAVE, true); expect(chdlr.sw.getId()).andReturn(1L).anyTimes(); @@ -1242,19 +1276,19 @@ public class ControllerTest extends FloodlightTestCase replay(chdlr.sw); chdlr.processOFMessage(msg); verify(chdlr.sw); - assertTrue("activeSwitches must be empty", + assertTrue("activeSwitches must be empty", controller.activeSwitches.isEmpty()); } - + @Test /** Equal2Master transition ==> no change */ public void testNiciraRoleReplyEqual2Master() throws Exception{ int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); - OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, + OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_MASTER); - - setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.MASTER, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.MASTER, true); expect(chdlr.sw.getId()).andReturn(1L).anyTimes(); @@ -1268,16 +1302,16 @@ public class ControllerTest extends FloodlightTestCase assertSame("activeSwitches must contain this switch", chdlr.sw, controller.activeSwitches.get(1L)); } - - @Test - public void testNiciraRoleReplyMaster2Slave() + + @Test + public void testNiciraRoleReplyMaster2Slave() throws Exception { int xid = 424242; Controller.OFChannelHandler chdlr = getChannelHandlerForRoleReplyTest(); - OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, + OFVendor msg = getRoleReplyMsgForRoleReplyTest(xid, OFRoleReplyVendorData.NX_ROLE_SLAVE); - - setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); + + setupPendingRoleRequest(chdlr.sw, xid, Role.SLAVE, 123456); expect(chdlr.sw.getHARole()).andReturn(null); chdlr.sw.setHARole(Role.SLAVE, true); expect(chdlr.sw.getId()).andReturn(1L).anyTimes(); @@ -1290,10 +1324,10 @@ public class ControllerTest extends FloodlightTestCase replay(chdlr.sw); chdlr.processOFMessage(msg); verify(chdlr.sw); - assertTrue("activeSwitches must be empty", + assertTrue("activeSwitches must be empty", controller.activeSwitches.isEmpty()); } - + /** * Tests that you can't remove a switch from the active * switch list. @@ -1314,7 +1348,7 @@ public class ControllerTest extends FloodlightTestCase assertTrue(exceptionThrown); verify(sw); } - + public void verifyPortChangedUpdateInQueue(IOFSwitch sw) throws Exception { assertEquals(1, controller.updates.size()); IUpdate update = controller.updates.take(); @@ -1323,21 +1357,21 @@ public class ControllerTest extends FloodlightTestCase assertEquals(sw, swUpdate.sw); assertEquals(SwitchUpdateType.PORTCHANGED, swUpdate.switchUpdateType); } - + /* * Test handlePortStatus() * TODO: test correct updateStorage behavior! */ - @Test + @Test public void testHandlePortStatus() throws Exception { IOFSwitch sw = createMock(IOFSwitch.class); OFPhysicalPort port = new OFPhysicalPort(); port.setName("myPortName1"); port.setPortNumber((short)42); - + OFPortStatus ofps = new OFPortStatus(); ofps.setDesc(port); - + ofps.setReason((byte)OFPortReason.OFPPR_ADD.ordinal()); sw.setPort(port); expectLastCall().once(); @@ -1346,7 +1380,7 @@ public class ControllerTest extends FloodlightTestCase verify(sw); verifyPortChangedUpdateInQueue(sw); reset(sw); - + ofps.setReason((byte)OFPortReason.OFPPR_MODIFY.ordinal()); sw.setPort(port); expectLastCall().once(); @@ -1355,7 +1389,7 @@ public class ControllerTest extends FloodlightTestCase verify(sw); verifyPortChangedUpdateInQueue(sw); reset(sw); - + ofps.setReason((byte)OFPortReason.OFPPR_DELETE.ordinal()); sw.deletePort(port.getPortNumber()); expectLastCall().once(); @@ -1386,8 +1420,8 @@ public class ControllerTest extends FloodlightTestCase } return null; } - - private void setupSwitchForDispatchTest(IOFSwitch sw, + + private void setupSwitchForDispatchTest(IOFSwitch sw, boolean isConnected, Role role) { Lock lock = createNiceMock(Lock.class); @@ -1397,10 +1431,10 @@ public class ControllerTest extends FloodlightTestCase expect(sw.isConnected()).andReturn(isConnected).anyTimes(); expect(sw.getHARole()).andReturn(role).anyTimes(); replay(lock); - + } - - @Test + + @Test public void testMessageDispatch() throws Exception { // Mock a dummy packet in // Build our test packet @@ -1421,7 +1455,7 @@ public class ControllerTest extends FloodlightTestCase .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - // Build the PacketIn + // Build the PacketIn OFPacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) @@ -1429,60 +1463,60 @@ public class ControllerTest extends FloodlightTestCase .setReason(OFPacketInReason.NO_MATCH) .setTotalLength((short) testPacketSerialized.length); - - // Mock switch and add to data structures + + // Mock switch and add to data structures IOFSwitch sw = createMock(IOFSwitch.class); - + controller.connectedSwitches.add(sw); - - // create a channel handler + + // create a channel handler OFChannelState state = new OFChannelState(); state.hsState = HandshakeState.READY; Controller.OFChannelHandler chdlr = controller.new OFChannelHandler(state); chdlr.sw = sw; - - // mock role changer - RoleChanger roleChanger = createMock(RoleChanger.class); - roleChanger.submitRequest(eq(controller.connectedSwitches), + + // mock role changer + RoleChanger roleChanger = createMock(RoleChanger.class); + roleChanger.submitRequest(eq(controller.connectedSwitches), anyObject(Role.class)); expectLastCall().anyTimes(); controller.roleChanger = roleChanger; - - - // Mock message listener and add + + + // Mock message listener and add IOFMessageListener listener = createNiceMock(IOFMessageListener.class); expect(listener.getName()).andReturn("foobar").anyTimes(); replay(listener); controller.addOFMessageListener(OFType.PACKET_IN, listener); resetToStrict(listener); - - - assertEquals("Check that update queue is empty", 0, + + + assertEquals("Check that update queue is empty", 0, controller.updates.size()); - - + + replay(roleChanger); - + //------------------- // Test 1: role is master, switch is master and in activeMap - // we expect the msg to be dispatched + // we expect the msg to be dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); setupSwitchForDispatchTest(sw, true, Role.MASTER); - listener.receive(same(sw), same(pi), + listener.receive(same(sw), same(pi), anyObject(FloodlightContext.class)); expectLastCall().andReturn(Command.STOP).once(); replay(sw, listener); chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(0, controller.updates.size()); - - + + //------------------- // Test 1b: role is master, switch is master and in activeMap - // but switch is not connected - // no message dispatched + // but switch is not connected + // no message dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); @@ -1491,11 +1525,11 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(0, controller.updates.size()); - - + + //------------------- // Test 1c: role is master, switch is slave and in activeMap - // no message dispatched + // no message dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); @@ -1504,11 +1538,11 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(0, controller.updates.size()); - - + + //------------------- // Test 1d: role is master, switch is master but not in activeMap - // we expect the msg to be dispatched + // we expect the msg to be dispatched reset(sw); resetToDefault(listener); controller.activeSwitches.remove(1L); @@ -1516,30 +1550,30 @@ public class ControllerTest extends FloodlightTestCase replay(sw, listener); chdlr.processOFMessage(pi); verify(sw, listener); - assertEquals(0, controller.updates.size()); - - - + assertEquals(0, controller.updates.size()); + + + //------------------- // Test 2: check correct dispatch and HA notification behavior - // We set the role to slave but do not notify the clients + // We set the role to slave but do not notify the clients reset(sw); resetToDefault(listener); controller.activeSwitches.put(1L, sw); setupSwitchForDispatchTest(sw, true, Role.MASTER); - listener.receive(same(sw), same(pi), + listener.receive(same(sw), same(pi), anyObject(FloodlightContext.class)); expectLastCall().andReturn(Command.STOP).once(); replay(sw, listener); - controller.setRole(Role.SLAVE); + controller.setRole(Role.SLAVE, "Testing"); controller.doSetRole(); // avoid the wait chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(1, controller.updates.size()); - - // Now notify listeners + + // Now notify listeners Controller.IUpdate upd = controller.updates.poll(1, TimeUnit.NANOSECONDS); - assertTrue("Check that update is HARoleUpdate", + assertTrue("Check that update is HARoleUpdate", upd instanceof Controller.HARoleUpdate); upd.dispatch(); resetToDefault(listener); @@ -1547,49 +1581,49 @@ public class ControllerTest extends FloodlightTestCase chdlr.processOFMessage(pi); verify(listener); assertEquals(0, controller.updates.size()); - + // transition back to master but don't notify yet resetToDefault(listener); replay(listener); - controller.setRole(Role.MASTER); + controller.setRole(Role.MASTER, "Testing"); controller.doSetRole(); // avoid the wait chdlr.processOFMessage(pi); verify(listener); assertEquals(1, controller.updates.size()); - - // now notify listeners + + // now notify listeners upd = controller.updates.poll(1, TimeUnit.NANOSECONDS); - assertTrue("Check that update is HARoleUpdate", + assertTrue("Check that update is HARoleUpdate", upd instanceof Controller.HARoleUpdate); upd.dispatch(); resetToDefault(listener); - listener.receive(same(sw), same(pi), + listener.receive(same(sw), same(pi), anyObject(FloodlightContext.class)); expectLastCall().andReturn(Command.STOP).once(); replay(listener); chdlr.processOFMessage(pi); verify(listener); assertEquals(0, controller.updates.size()); - + verify(sw); } - - + + /* * Test correct timing behavior between HA Role notification and dispatching - * OFMessages to listeners. + * OFMessages to listeners. * When transitioning to SLAVE: stop dispatching message before sending * notifications * When transitioning to MASTER: start dispatching messages after sending * notifications - * (This implies that messages should not be dispatched while the - * notifications are being sent). - * + * (This implies that messages should not be dispatched while the + * notifications are being sent). + * * We encapsulate the logic for this in a class that implements both * IHAListener and IOFMessageListener. Then we inject an OFMessage fom - * the IHAListener and check that it gets dropped correctly. + * the IHAListener and check that it gets dropped correctly. */ - @Test + @Test public void testRoleNotifcationAndMessageDispatch() throws Exception { class TestRoleNotificationsAndDispatch implements IHAListener, IOFMessageListener { OFPacketIn pi; @@ -1597,7 +1631,7 @@ public class ControllerTest extends FloodlightTestCase IOFSwitch sw; private boolean haveReceived; private boolean doInjectMessageFromHAListener; - + public TestRoleNotificationsAndDispatch() { IPacket testPacket = new Ethernet() .setSourceMACAddress("00:44:33:22:11:00") @@ -1615,78 +1649,78 @@ public class ControllerTest extends FloodlightTestCase .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); byte[] testPacketSerialized = testPacket.serialize(); - - // Build the PacketIn + + // Build the PacketIn pi = ((OFPacketIn) new BasicFactory().getMessage(OFType.PACKET_IN)) .setBufferId(-1) .setInPort((short) 1) .setPacketData(testPacketSerialized) .setReason(OFPacketInReason.NO_MATCH) .setTotalLength((short) testPacketSerialized.length); - - // Mock switch and add to data structures + + // Mock switch and add to data structures sw = createMock(IOFSwitch.class); controller.connectedSwitches.add(sw); controller.activeSwitches.put(1L, sw); setupSwitchForDispatchTest(sw, true, Role.MASTER); replay(sw); - - // create a channel handler + + // create a channel handler OFChannelState state = new OFChannelState(); state.hsState = HandshakeState.READY; chdlr = controller.new OFChannelHandler(state); chdlr.sw = this.sw; - + // add ourself as listeners controller.addOFMessageListener(OFType.PACKET_IN, this); controller.addHAListener(this); } - - + + private void injectMessage(boolean shouldReceive) throws Exception { haveReceived = false; chdlr.processOFMessage(pi); assertEquals(shouldReceive, haveReceived); } - + public void transitionToSlave() throws Exception { IUpdate update; - + // Bring controller into well defined state for MASTER - doInjectMessageFromHAListener = false; + doInjectMessageFromHAListener = false; update = controller.new HARoleUpdate(Role.MASTER, Role.SLAVE); update.dispatch(); doInjectMessageFromHAListener = true; - - - // inject message. Listener called + + + // inject message. Listener called injectMessage(true); - // Dispatch update + // Dispatch update update = controller.new HARoleUpdate(Role.SLAVE, Role.MASTER); update.dispatch(); // inject message. Listener not called injectMessage(false); } - + public void transitionToMaster() throws Exception { IUpdate update; - + // Bring controller into well defined state for SLAVE - doInjectMessageFromHAListener = false; + doInjectMessageFromHAListener = false; update = controller.new HARoleUpdate(Role.SLAVE, Role.MASTER); update.dispatch(); doInjectMessageFromHAListener = true; - - - // inject message. Listener not called + + + // inject message. Listener not called injectMessage(false); - // Dispatch update + // Dispatch update update = controller.new HARoleUpdate(Role.MASTER, Role.SLAVE); update.dispatch(); // inject message. Listener called injectMessage(true); } - + //--------------- // IHAListener //--------------- @@ -1700,7 +1734,7 @@ public class ControllerTest extends FloodlightTestCase e.printStackTrace(); } } - + @Override public void @@ -1709,7 +1743,7 @@ public class ControllerTest extends FloodlightTestCase Map<String, String> removedControllerNodeIPs) { // TODO Auto-generated method stub } - + //------------------------- // IOFMessageListener //------------------------- @@ -1726,18 +1760,18 @@ public class ControllerTest extends FloodlightTestCase return false; } @Override - public Command receive(IOFSwitch sw, - OFMessage msg, + public Command receive(IOFSwitch sw, + OFMessage msg, FloodlightContext cntx) { haveReceived = true; return Command.STOP; } } - + TestRoleNotificationsAndDispatch x = new TestRoleNotificationsAndDispatch(); x.transitionToSlave(); x.transitionToMaster(); - + } } diff --git a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java index 301832f6a9400f29f28a0d7c6fa9c871e2ffc449..7b5cd6293c1888250299c6352918c610df70733c 100644 --- a/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java +++ b/src/test/java/net/floodlightcontroller/core/test/MockFloodlightProvider.java @@ -17,6 +17,8 @@ package net.floodlightcontroller.core.test; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -39,6 +41,7 @@ import net.floodlightcontroller.core.IOFSwitchDriver; import net.floodlightcontroller.core.IOFSwitchFilter; import net.floodlightcontroller.core.IOFSwitchListener; import net.floodlightcontroller.core.IListener.Command; +import net.floodlightcontroller.core.RoleInfo; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.core.module.IFloodlightModule; @@ -102,6 +105,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro /** * @return the listeners */ + @Override public Map<OFType, List<IOFMessageListener>> getListeners() { Map<OFType, List<IOFMessageListener>> lers = new HashMap<OFType, List<IOFMessageListener>>(); @@ -158,6 +162,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro } } + @Override public void handleOutgoingMessage(IOFSwitch sw, OFMessage m, FloodlightContext bc) { List<IOFMessageListener> msgListeners = null; if (listeners.containsKey(m.getType())) { @@ -191,6 +196,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro return switchListeners; } + @Override public void terminate() { } @@ -289,7 +295,7 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro } @Override - public void setRole(Role role) { + public void setRole(Role role, String roleChangeDescription) { } @@ -304,10 +310,6 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro } } - @Override - public String getControllerId() { - return "localhost"; - } @Override public Map<String, String> getControllerNodeIPs() { @@ -355,4 +357,24 @@ public class MockFloodlightProvider implements IFloodlightModule, IFloodlightPro } + @Override + public RoleInfo getRoleInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map<String, Long> getMemory() { + Map<String, Long> m = new HashMap<String, Long>(); + Runtime runtime = Runtime.getRuntime(); + m.put("total", runtime.totalMemory()); + m.put("free", runtime.freeMemory()); + return m; + } + + @Override + public Long getUptime() { + RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); + return rb.getUptime(); + } } diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 671ef50609cd3022dfbb5b31f199f377cbb750c9..40986e9f2577af119523534ac3762404bb66c6f0 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -95,7 +95,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase { testARPReplyPacket_3; protected IPacket testARPReqPacket_1, testARPReqPacket_2; protected byte[] testARPReplyPacket_1_Srld, testARPReplyPacket_2_Srld; - private byte[] testARPReplyPacket_3_Serialized; DeviceManagerImpl deviceManager; MemoryStorageSource storageSource; FlowReconcileManager flowReconcileMgr; @@ -185,9 +184,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Another test packet with a different source IP this.testARPReplyPacket_2 = new Ethernet() - .setSourceMACAddress("00:44:33:22:11:01") + .setSourceMACAddress("00:99:88:77:66:55") .setDestinationMACAddress("00:11:22:33:44:55") .setEtherType(Ethernet.TYPE_ARP) + .setVlanID((short)5) .setPayload( new ARP() .setHardwareType(ARP.HW_TYPE_ETHERNET) @@ -201,23 +201,6 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); this.testARPReplyPacket_2_Srld = testARPReplyPacket_2.serialize(); - this.testARPReplyPacket_3 = new Ethernet() - .setSourceMACAddress("00:44:33:22:11:01") - .setDestinationMACAddress("00:11:22:33:44:55") - .setEtherType(Ethernet.TYPE_ARP) - .setPayload( - new ARP() - .setHardwareType(ARP.HW_TYPE_ETHERNET) - .setProtocolType(ARP.PROTO_TYPE_IP) - .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.3")) - .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) - .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); - this.testARPReplyPacket_3_Serialized = testARPReplyPacket_3.serialize(); - // Build the PacketIn this.packetIn_1 = ((OFPacketIn) mockFloodlightProvider. getOFMessageFactory().getMessage(OFType.PACKET_IN)) @@ -235,21 +218,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .setPacketData(this.testARPReplyPacket_2_Srld) .setReason(OFPacketInReason.NO_MATCH) .setTotalLength((short) this.testARPReplyPacket_2_Srld.length); - - // Build the PacketIn - this.packetIn_3 = ((OFPacketIn) mockFloodlightProvider. - getOFMessageFactory().getMessage(OFType.PACKET_IN)) - .setBufferId(-1) - .setInPort((short) 1) - .setPacketData(this.testARPReplyPacket_3_Serialized) - .setReason(OFPacketInReason.NO_MATCH) - .setTotalLength((short) this.testARPReplyPacket_3_Serialized.length); } - + @Test public void testLastSeen() throws Exception { Calendar c = Calendar.getInstance(); @@ -423,11 +397,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(4, deviceManager.getAllDevices().size()); verify(mockListener); - - + + reset(mockListener); replay(mockListener); - + reset(deviceManager.topology); deviceManager.topology.addListener(deviceManager); expectLastCall().times(1); @@ -437,16 +411,16 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.startUp(null); Entity entityNoClass = new Entity(5L, (short)1, 5, -1L, 1, new Date()); assertEquals(null, deviceManager.learnDeviceByEntity(entityNoClass)); - + verify(mockListener); } - - + + private void doTestEntityOrdering(boolean computeInsertionPoint) throws Exception { Entity e = new Entity(10L, null, null, null, null, null); IEntityClass ec = createNiceMock(IEntityClass.class); Device d = new Device(deviceManager, 1L, e, ec); - + int expectedLength = 1; Long[] macs = new Long[] { 5L, // new first element 15L, // new last element @@ -457,7 +431,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { 1L, 20L }; - + for (Long mac: macs) { e = new Entity(mac, null, null, null, null, null); int insertionPoint; @@ -473,12 +447,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(-1, d.entities[i].compareTo(d.entities[i+1])); } } - + @Test public void testEntityOrderingExternal() throws Exception { doTestEntityOrdering(true); } - + @Test public void testEntityOrderingInternal() throws Exception { doTestEntityOrdering(false); @@ -594,12 +568,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertArrayEquals(new Integer[] { 1 }, ips); verify(mockListener); } - + private void verifyEntityArray(Entity[] expected, Device d) { Arrays.sort(expected); assertArrayEquals(expected, d.entities); } - + @Test public void testNoLearningOnInternalPorts() throws Exception { IDeviceListener mockListener = @@ -636,7 +610,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Switches 2 and 4 have only internal ports expect(mockTopology.isAttachmentPointPort(or(eq(2L), eq(4L)), anyShort())) .andReturn(false).anyTimes(); - + expect(mockTopology.isConsistent(1L, (short)1, 3L, (short)1)) .andReturn(false).once(); @@ -664,13 +638,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase { mockListener.deviceAdded(isA(IDevice.class)); expectLastCall().once(); replay(mockListener); - + // cannot learn device internal ports d = deviceManager.learnDeviceByEntity(entity2); assertNull(d); d = deviceManager.learnDeviceByEntity(entity4); assertNull(d); - + d = deviceManager.learnDeviceByEntity(entity1); assertEquals(1, deviceManager.getAllDevices().size()); aps = d.getAttachmentPoints(); @@ -679,10 +653,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { ips = d.getIPv4Addresses(); assertArrayEquals(new Integer[] { 1 }, ips); verify(mockListener); - + reset(mockListener); replay(mockListener); - + // don't learn d = deviceManager.learnDeviceByEntity(entity2); assertEquals(1, deviceManager.getAllDevices().size()); @@ -692,12 +666,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { ips = d.getIPv4Addresses(); assertArrayEquals(new Integer[] { 1 }, ips); verify(mockListener); - + reset(mockListener); mockListener.deviceMoved(isA(IDevice.class)); mockListener.deviceIPV4AddrChanged(isA(IDevice.class)); replay(mockListener); - + // learn d = deviceManager.learnDeviceByEntity(entity3); assertEquals(1, deviceManager.getAllDevices().size()); @@ -708,10 +682,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Arrays.sort(ips); assertArrayEquals(new Integer[] { 1, 3 }, ips); verify(mockListener); - + reset(mockListener); replay(mockListener); - + // don't learn d = deviceManager.learnDeviceByEntity(entity4); assertEquals(1, deviceManager.getAllDevices().size()); @@ -723,7 +697,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertArrayEquals(new Integer[] { 1, 3 }, ips); verify(mockListener); } - + @Test public void testAttachmentPointSuppression() throws Exception { IDeviceListener mockListener = @@ -773,6 +747,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Calendar c = Calendar.getInstance(); Entity entity0 = new Entity(1L, null, null, null, null, c.getTime()); + // No attachment point should be learnt on 1L, 1 Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime()); c.add(Calendar.SECOND, 1); Entity entity2 = new Entity(1L, null, 1, 5L, 1, c.getTime()); @@ -788,7 +763,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { mockListener.deviceAdded(isA(IDevice.class)); 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); @@ -805,10 +780,10 @@ public class DeviceManagerImplTest extends FloodlightTestCase { verify(mockListener); reset(mockListener); - mockListener.deviceMoved((isA(IDevice.class))); + //mockListener.deviceMoved((isA(IDevice.class))); //mockListener.deviceIPV4AddrChanged((isA(IDevice.class))); replay(mockListener); - + // there is no device moved because entity 1 was not learned due to suppression d = deviceManager.learnDeviceByEntity(entity2); assertEquals(1, deviceManager.getAllDevices().size()); aps = d.getAttachmentPoints(); @@ -968,23 +943,19 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertArrayEquals(new SwitchPort[] { new SwitchPort(1L, OFPort.OFPP_LOCAL.getValue()) }, aps); } - @Test - public void testPacketIn() throws Exception { - byte[] dataLayerSource = - ((Ethernet)this.testARPReplyPacket_1).getSourceMACAddress(); - + public void testPacketInBasic(byte[] deviceMac, OFPacketIn packetIn) { // Mock up our expected behavior ITopologyService mockTopology = createMock(ITopologyService.class); deviceManager.topology = mockTopology; expect(mockTopology.isAttachmentPointPort(EasyMock.anyLong(), - EasyMock.anyShort())). - andReturn(true).anyTimes(); + EasyMock.anyShort())). + andReturn(true).anyTimes(); expect(mockTopology.isConsistent(EasyMock.anyLong(), - EasyMock.anyShort(), - EasyMock.anyLong(), - EasyMock.anyShort())).andReturn(false). - anyTimes(); + EasyMock.anyShort(), + EasyMock.anyLong(), + EasyMock.anyShort())).andReturn(false). + anyTimes(); expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes(); replay(mockTopology); @@ -994,29 +965,26 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Integer ipaddr = IPv4.toIPv4Address("192.168.1.1"); Device device = new Device(deviceManager, - new Long(deviceManager.deviceKeyCounter), - new Entity(Ethernet.toLong(dataLayerSource), - (short)5, - ipaddr, - 1L, - 1, - currentDate), - DefaultEntityClassifier.entityClass); - - - + new Long(deviceManager.deviceKeyCounter), + new Entity(Ethernet.toLong(deviceMac), + (short)5, + ipaddr, + 1L, + 1, + currentDate), + DefaultEntityClassifier.entityClass); // Get the listener and trigger the packet in IOFSwitch switch1 = mockFloodlightProvider.getSwitches().get(1L); - mockFloodlightProvider.dispatchMessage(switch1, this.packetIn_1); + mockFloodlightProvider.dispatchMessage(switch1, packetIn); // Verify the replay matched our expectations // verify(mockTopology); // Verify the device Device rdevice = (Device) - deviceManager.findDevice(Ethernet.toLong(dataLayerSource), - (short)5, null, null, null); + deviceManager.findDevice(Ethernet.toLong(deviceMac), + (short)5, null, null, null); assertEquals(device, rdevice); assertEquals(new Short((short)5), rdevice.getVlanId()[0]); @@ -1024,8 +992,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Device result = null; Iterator<? extends IDevice> dstiter = deviceManager.queryClassDevices(device.getEntityClass(), - null, null, ipaddr, - null, null); + null, null, ipaddr, + null, null); if (dstiter.hasNext()) { result = (Device)dstiter.next(); } @@ -1034,32 +1002,32 @@ public class DeviceManagerImplTest extends FloodlightTestCase { device = new Device(device, - new Entity(Ethernet.toLong(dataLayerSource), - (short)5, - ipaddr, - 5L, - 2, - currentDate), - -1); + new Entity(Ethernet.toLong(deviceMac), + (short)5, + ipaddr, + 5L, + 2, + currentDate), + -1); reset(mockTopology); expect(mockTopology.isAttachmentPointPort(anyLong(), - anyShort())). - andReturn(true). - anyTimes(); + anyShort())). + andReturn(true). + anyTimes(); expect(mockTopology.isConsistent(EasyMock.anyLong(), - EasyMock.anyShort(), - EasyMock.anyLong(), - EasyMock.anyShort())).andReturn(false). - anyTimes(); + EasyMock.anyShort(), + EasyMock.anyLong(), + EasyMock.anyShort())).andReturn(false). + anyTimes(); expect(mockTopology.isBroadcastDomainPort(EasyMock.anyLong(), - EasyMock.anyShort())) - .andReturn(false) - .anyTimes(); + EasyMock.anyShort())) + .andReturn(false) + .anyTimes(); expect(mockTopology.getL2DomainId(1L)).andReturn(1L).anyTimes(); expect(mockTopology.getL2DomainId(5L)).andReturn(1L).anyTimes(); expect(mockTopology.isInSameBroadcastDomain(1L, (short)1, 5L, (short)2)). - andReturn(false).anyTimes(); + andReturn(false).anyTimes(); // Start recording the replay on the mocks replay(mockTopology); @@ -1073,12 +1041,36 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // Verify the device rdevice = (Device) - deviceManager.findDevice(Ethernet.toLong(dataLayerSource), - (short)5, null, null, null); + deviceManager.findDevice(Ethernet.toLong(deviceMac), + (short)5, null, null, null); assertEquals(device, rdevice); } - + @Test + public void testPacketIn() throws Exception { + byte[] deviceMac1 = + ((Ethernet)this.testARPReplyPacket_1).getSourceMACAddress(); + testPacketInBasic(deviceMac1, packetIn_1); + } + + /** + * This test ensures the device manager learns the source device + * corresponding to the senderHardwareAddress and senderProtocolAddress + * in an ARP response whenever the senderHardwareAddress is different + * from the source MAC address of the Ethernet frame. + * + * This test is the same as testPacketIn method, except for the + * packet-in that's used. + * @throws Exception + */ + @Test + public void testDeviceLearningFromArpResponseData() throws Exception { + ARP arp = (ARP)((Ethernet)this.testARPReplyPacket_2).getPayload(); + byte[] deviceMac2 = arp.getSenderHardwareAddress(); + + testPacketInBasic(deviceMac2, packetIn_2); + } + /** * Note: Entity expiration does not result in device moved notification. * @throws Exception @@ -1175,12 +1167,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .andReturn(false).atLeastOnce(); expect(mockListener.isCallbackOrderingPrereq((String)anyObject(), (String)anyObject())) .andReturn(false).atLeastOnce(); - + Calendar c = Calendar.getInstance(); c.add(Calendar.MILLISECOND, -DeviceManagerImpl.ENTITY_TIMEOUT-1); Entity entity1 = new Entity(1L, null, 1, 1L, 1, c.getTime()); Entity entity2 = new Entity(1L, null, 2, 5L, 1, c.getTime()); - + ITopologyService mockTopology = createMock(ITopologyService.class); deviceManager.topology = mockTopology; @@ -1222,16 +1214,16 @@ public class DeviceManagerImplTest extends FloodlightTestCase { r = deviceManager.findDevice(1L, null, null, null, null); assertNull(r); - + verify(mockListener); } - + /* * A ConcurrentHashMap for devices (deviceMap) that can be used to test * code that specially handles concurrent modification situations. In * particular, we overwrite values() and will replace / remove all the * elements returned by values. - * + * * The remove flag in the constructor specifies if devices returned by * values() should be removed or replaced. */ @@ -1243,7 +1235,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { super(); this.remove = remove; } - + @Override public Collection<Device> values() { // Get the values from the real map and copy them since @@ -1285,17 +1277,17 @@ public class DeviceManagerImplTest extends FloodlightTestCase { return devs; } } - + @Test public void testEntityExpiration() throws Exception { doTestEntityExpiration(); } - + @Test public void testDeviceExpiration() throws Exception { doTestDeviceExpiration(); } - + /* Test correct entity cleanup behavior when a concurrent modification * occurs. */ @@ -1304,7 +1296,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false); doTestEntityExpiration(); } - + /* Test correct entity cleanup behavior when a concurrent remove * occurs. */ @@ -1313,7 +1305,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(true); doTestDeviceExpiration(); } - + /* Test correct entity cleanup behavior when a concurrent modification * occurs. */ @@ -1322,7 +1314,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.deviceMap = new ConcurrentlyModifiedDeviceMap(false); doTestDeviceExpiration(); } - + @Test public void testAttachmentPointFlapping() throws Exception { @@ -1591,7 +1583,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { andReturn(true).anyTimes(); expect(mockTopology.getL2DomainId(EasyMock.anyLong())).andReturn(1L).anyTimes(); replay(mockTopology); - + doTestDeviceQuery(); } @@ -1695,19 +1687,19 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date()); Entity entity2 = new Entity(2L, (short)2, 2, 1L, 2, new Date()); Entity entity2b = new Entity(22L, (short)2, 2, 1L, 2, new Date()); - + Entity entity3 = new Entity(3L, (short)1, 3, 2L, 1, new Date()); Entity entity4 = new Entity(4L, (short)2, 4, 2L, 2, new Date()); - + Entity entity5 = new Entity(5L, (short)1, 5, 3L, 1, new Date()); - + IDevice d1 = deviceManager.learnDeviceByEntity(entity1); IDevice d2 = deviceManager.learnDeviceByEntity(entity2); IDevice d3 = deviceManager.learnDeviceByEntity(entity3); IDevice d4 = deviceManager.learnDeviceByEntity(entity4); IDevice d5 = deviceManager.learnDeviceByEntity(entity5); - + // Make sure the entity classifier worked as expected assertEquals(MockEntityClassifierMac.testECMac1, d1.getEntityClass()); assertEquals(MockEntityClassifierMac.testECMac1, d2.getEntityClass()); @@ -1715,7 +1707,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(MockEntityClassifierMac.testECMac2, d4.getEntityClass()); assertEquals(DefaultEntityClassifier.entityClass, d5.getEntityClass()); - + // Look up the device using findDevice() which uses only the primary // index assertEquals(d1, deviceManager.findDevice(entity1.getMacAddress(), @@ -1782,12 +1774,12 @@ public class DeviceManagerImplTest extends FloodlightTestCase { } if (!exceptionCaught) fail("findDevice() did not throw IllegalArgumentException"); - - + + Entity entityNoClass = new Entity(5L, (short)1, 5, -1L, 1, new Date()); assertEquals(null, deviceManager.findDeviceByEntity(entityNoClass)); - - + + // Now look up destination devices assertEquals(d1, deviceManager.findClassDevice(d2.getEntityClass(), entity1.getMacAddress(), @@ -1802,11 +1794,13 @@ public class DeviceManagerImplTest extends FloodlightTestCase { (short) -1, 0)); } - + + + @Test public void testGetIPv4Addresses() { // Looks like Date is only 1s granularity - + ITopologyService mockTopology = createMock(ITopologyService.class); deviceManager.topology = mockTopology; expect(mockTopology.isAttachmentPointPort(anyLong(), @@ -1833,8 +1827,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Entity e1 = new Entity(1L, (short)1, null, null, null, new Date(2000)); Device d1 = deviceManager.learnDeviceByEntity(e1); assertArrayEquals(new Integer[0], d1.getIPv4Addresses()); - - + + Entity e2 = new Entity(2L, (short)2, 2, null, null, new Date(2000)); Device d2 = deviceManager.learnDeviceByEntity(e2); d2 = deviceManager.learnDeviceByEntity(e2); @@ -1849,7 +1843,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d2 = deviceManager.learnDeviceByEntity(e2c); assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses()); assertEquals(3, d2.entities.length); - + // Other devices with different IPs shouldn't interfere Entity e3 = new Entity(3L, (short)3, 3, null, null, new Date(4000)); Entity e3b = new Entity(3L, (short)3, 3, 3L, 3, new Date(4400)); @@ -1857,14 +1851,14 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d3 = deviceManager.learnDeviceByEntity(e3b); assertArrayEquals(new Integer[] { 2 }, d2.getIPv4Addresses()); assertArrayEquals(new Integer[] { 3 }, d3.getIPv4Addresses()); - + // Add another IP to d3 Entity e3c = new Entity(3L, (short)3, 33, 3L, 3, new Date(4400)); d3 = deviceManager.learnDeviceByEntity(e3c); Integer[] ips = d3.getIPv4Addresses(); Arrays.sort(ips); assertArrayEquals(new Integer[] { 3, 33 }, ips); - + // Add another device that also claims IP2 but is older than e2 Entity e4 = new Entity(4L, (short)4, 2, null, null, new Date(1000)); Entity e4b = new Entity(4L, (short)4, null, 4L, 4, new Date(1000)); @@ -1874,7 +1868,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { // add another entity to d4 d4 = deviceManager.learnDeviceByEntity(e4b); assertArrayEquals(new Integer[0], d4.getIPv4Addresses()); - + // Make e4 and e4a newer Entity e4c = new Entity(4L, (short)4, 2, null, null, new Date(5000)); Entity e4d = new Entity(4L, (short)4, null, 4L, 5, new Date(5000)); @@ -1883,7 +1877,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertArrayEquals(new Integer[0], d2.getIPv4Addresses()); // FIXME: d4 should not return IP4 assertArrayEquals(new Integer[] { 2 }, d4.getIPv4Addresses()); - + // Add another newer entity to d2 but with different IP Entity e2d = new Entity(2L, (short)2, 22, 4L, 6, new Date(6000)); d2 = deviceManager.learnDeviceByEntity(e2d); @@ -1901,7 +1895,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { ips= d4.getIPv4Addresses(); Arrays.sort(ips); assertArrayEquals(new Integer[] { 2, 42 }, ips); - + // add a couple more IPs Entity e2f = new Entity(2L, (short)2, 4242, 2L, 5, new Date(8000)); d2 = deviceManager.learnDeviceByEntity(e2f); @@ -1914,7 +1908,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Arrays.sort(ips); assertArrayEquals(new Integer[] { 2, 42, 4242 }, ips); } - + // TODO: this test should really go into a separate class that collects // unit tests for Device @Test @@ -1941,7 +1935,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertArrayEquals(new Short[0], d.getSwitchPortVlanIds(swp2x1)); } - + @Test public void testReclassifyDevice() { MockFlexEntityClassifier flexClassifier = @@ -1966,20 +1960,20 @@ public class DeviceManagerImplTest extends FloodlightTestCase { .andReturn(false) .anyTimes(); replay(mockTopology); - + //flexClassifier.createTestEntityClass("Class1"); - + Entity entity1 = new Entity(1L, (short)1, 1, 1L, 1, new Date()); Entity entity1b = new Entity(1L, (short)2, 1, 1L, 1, new Date()); Entity entity2 = new Entity(2L, (short)1, 2, 2L, 2, new Date()); Entity entity2b = new Entity(2L, (short)2, 2, 2L, 2, new Date()); - - + + Device d1 = deviceManager.learnDeviceByEntity(entity1); Device d2 = deviceManager.learnDeviceByEntity(entity2); Device d1b = deviceManager.learnDeviceByEntity(entity1b); Device d2b = deviceManager.learnDeviceByEntity(entity2b); - + d1 = deviceManager.getDeviceIteratorForQuery(entity1.getMacAddress(), entity1.getVlan(), entity1.getIpv4Address(), entity1.getSwitchDPID(), entity1.getSwitchPort()) @@ -1987,9 +1981,9 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d1b = deviceManager.getDeviceIteratorForQuery(entity1b.getMacAddress(), entity1b.getVlan(), entity1b.getIpv4Address(), entity1b.getSwitchDPID(), entity1b.getSwitchPort()).next(); - + assertEquals(d1, d1b); - + d2 = deviceManager.getDeviceIteratorForQuery(entity2.getMacAddress(), entity2.getVlan(), entity2.getIpv4Address(), entity2.getSwitchDPID(), entity2.getSwitchPort()).next(); @@ -1997,32 +1991,32 @@ public class DeviceManagerImplTest extends FloodlightTestCase { entity2b.getVlan(), entity2b.getIpv4Address(), entity2b.getSwitchDPID(), entity2b.getSwitchPort()).next(); assertEquals(d2, d2b); - + IEntityClass eC1 = flexClassifier.createTestEntityClass("C1"); IEntityClass eC2 = flexClassifier.createTestEntityClass("C2"); - + flexClassifier.addVlanEntities((short)1, eC1); flexClassifier.addVlanEntities((short)2, eC1); - + deviceManager.reclassifyDevice(d1); deviceManager.reclassifyDevice(d2); - + d1 = deviceManager.deviceMap.get( deviceManager.primaryIndex.findByEntity(entity1)); d1b = deviceManager.deviceMap.get( deviceManager.primaryIndex.findByEntity(entity1b)); - + assertEquals(d1, d1b); - + d2 = deviceManager.deviceMap.get( deviceManager.primaryIndex.findByEntity(entity2)); d2b = deviceManager.deviceMap.get( deviceManager.primaryIndex.findByEntity(entity2b)); - + assertEquals(d2, d2b); - + flexClassifier.addVlanEntities((short)1, eC2); - + deviceManager.reclassifyDevice(d1); deviceManager.reclassifyDevice(d2); d1 = deviceManager.deviceMap.get( @@ -2033,11 +2027,11 @@ public class DeviceManagerImplTest extends FloodlightTestCase { deviceManager.primaryIndex.findByEntity(entity2)); d2b = deviceManager.deviceMap.get( deviceManager.primaryIndex.findByEntity(entity2b)); - + assertNotSame(d1, d1b); - + assertNotSame(d2, d2b); - + flexClassifier.addVlanEntities((short)1, eC1); deviceManager.reclassifyDevice(d1); deviceManager.reclassifyDevice(d2); @@ -2058,7 +2052,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { classState.classIndex.findByEntity(entity2b); assertEquals(deviceKey1, deviceKey1b); - + assertEquals(deviceKey2, deviceKey2b); diff --git a/src/test/java/net/floodlightcontroller/packet/IPv4Test.java b/src/test/java/net/floodlightcontroller/packet/IPv4Test.java index acea62d25f195b35f012b8714e70983adf84e8af..c62b77602b2124eb9305162ba9e0e7ccd48df6cf 100644 --- a/src/test/java/net/floodlightcontroller/packet/IPv4Test.java +++ b/src/test/java/net/floodlightcontroller/packet/IPv4Test.java @@ -107,6 +107,40 @@ public class IPv4Test { IPv4 packet = new IPv4(); packet.deserialize(pktSerializedPadded, 0, pktSerializedPadded.length); byte[] pktSerialized1 = packet.serialize(); + // Padding is ignored assertTrue(Arrays.equals(pktSerialized, pktSerialized1)); } + + @Test + public void testDeserializeFragment() { + // IP packet with more frag bit set + byte[] pktSerializedFirstFrag = new byte[] { 0x45, 0x00, + 0x00, 0x2e, 0x41, (byte) 0xbe, 0x20, 0x00, 0x40, 0x06, + (byte) 0xd4, (byte) 0xf0, (byte) 0xc0, (byte) 0xa8, 0x02, (byte) 0xdb, (byte) 0xd0, 0x55, + (byte) 0x90, 0x42, (byte) 0xd5, 0x48, 0x01, (byte) 0xbb, (byte) 0xe3, 0x50, + (byte) 0xb2, 0x2f, (byte) 0xfc, (byte) 0xf8, (byte) 0xa8, 0x2c, 0x50, 0x18, + (byte) 0xff, (byte) 0xff, 0x24, 0x3c, 0x00, 0x00, 0x14, 0x03, + 0x01, 0x00, 0x01, 0x01, + }; + IPv4 packet1 = new IPv4(); + packet1.deserialize(pktSerializedFirstFrag, 0, pktSerializedFirstFrag.length); + byte[] pktSerialized1 = packet1.serialize(); + assertTrue(packet1.isFragment()); + assertTrue(Arrays.equals(pktSerializedFirstFrag, pktSerialized1)); + + // IP packet with offset + byte[] pktSerializedLastFrag = new byte[] { 0x45, 0x00, + 0x00, 0x2e, 0x41, (byte) 0xbe, 0x01, 0x00, 0x40, 0x06, + (byte) 0xd4, (byte) 0xf0, (byte) 0xc0, (byte) 0xa8, 0x02, (byte) 0xdb, (byte) 0xd0, 0x55, + (byte) 0x90, 0x42, (byte) 0xd5, 0x48, 0x01, (byte) 0xbb, (byte) 0xe3, 0x50, + (byte) 0xb2, 0x2f, (byte) 0xfc, (byte) 0xf8, (byte) 0xa8, 0x2c, 0x50, 0x18, + (byte) 0xff, (byte) 0xff, 0x24, 0x3c, 0x00, 0x00, 0x14, 0x03, + 0x01, 0x00, 0x01, 0x01, + }; + IPv4 packet2 = new IPv4(); + packet2.deserialize(pktSerializedLastFrag, 0, pktSerializedLastFrag.length); + byte[] pktSerialized2 = packet2.serialize(); + assertTrue(packet2.isFragment()); + assertTrue(Arrays.equals(pktSerializedLastFrag, pktSerialized2)); + } } diff --git a/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java b/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java index 578cd9d1ae7fac34c1337529da23f9d5764e0301..d127181907f0ca7036132f039006fd89b5eb3653 100644 --- a/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java +++ b/src/test/java/net/floodlightcontroller/storage/tests/StorageTest.java @@ -142,6 +142,7 @@ public abstract class StorageTest extends FloodlightTestCase { public void setUp() throws Exception { super.setUp(); Set<String> indexedColumnNames = new HashSet<String>(); + indexedColumnNames.add(PERSON_FIRST_NAME); indexedColumnNames.add(PERSON_LAST_NAME); storageSource.setExceptionHandler(null); storageSource.createTable(PERSON_TABLE_NAME, indexedColumnNames); @@ -223,6 +224,38 @@ public abstract class StorageTest extends FloodlightTestCase { checkExpectedResults(resultSet, columnList, expectedResults); } + @Test + public void testEfficientOrQuery() { + String[] columnList = {PERSON_FIRST_NAME,PERSON_LAST_NAME}; + Object[][] expectedResults = { + {"John", "Smith"}, + {"Lisa", "Jones"}, + {"Susan", "Jones"} + }; + IResultSet resultSet = storageSource.executeQuery(PERSON_TABLE_NAME, columnList, + new CompoundPredicate(CompoundPredicate.Operator.OR, false, + new OperatorPredicate(PERSON_LAST_NAME, OperatorPredicate.Operator.EQ, "Jones"), + new OperatorPredicate(PERSON_LAST_NAME, OperatorPredicate.Operator.EQ, "Smith") + ), + new RowOrdering(PERSON_SSN)); + checkExpectedResults(resultSet, columnList, expectedResults); + } + + @Test + public void testEfficientAndQuery() { + String[] columnList = {PERSON_FIRST_NAME,PERSON_LAST_NAME}; + Object[][] expectedResults = { + {"Lisa", "Jones"} + }; + IResultSet resultSet = storageSource.executeQuery(PERSON_TABLE_NAME, columnList, + new CompoundPredicate(CompoundPredicate.Operator.AND, false, + new OperatorPredicate(PERSON_LAST_NAME, OperatorPredicate.Operator.EQ, "Jones"), + new OperatorPredicate(PERSON_FIRST_NAME, OperatorPredicate.Operator.EQ, "Lisa") + ), + new RowOrdering(PERSON_SSN)); + checkExpectedResults(resultSet, columnList, expectedResults); + } + @Test public void testOrQuery() { String[] columnList = {PERSON_FIRST_NAME,PERSON_LAST_NAME, PERSON_AGE};