diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index a33f1410419c5c9b9fe7cc6a9c977a8f0768c4a9..e5d7ad28cd0fbb71980064d0a950f3793e788af5 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -275,6 +275,15 @@ public class Controller implements IFloodlightProviderService, this.newRole = newRole; } public void dispatch() { + // Make sure that old and new roles are different. + if (oldRole == newRole) { + if (log.isTraceEnabled()) { + log.trace("HA role update ignored as the old and " + + "new roles are the same. newRole = {}" + + "oldRole = {}", newRole, oldRole); + } + return; + } if (log.isTraceEnabled()) { log.trace("Dispatching HA Role update newRole = {}, oldRole = {}", newRole, oldRole); @@ -354,6 +363,10 @@ public class Controller implements IFloodlightProviderService, @Override public void setRole(Role role) { if (role == null) throw new NullPointerException("Role can not be null."); + if (role == Role.MASTER && this.role == Role.SLAVE) { + // Reset db state to Inactive for all switches. + updateAllInactiveSwitchInfo(); + } // Need to synchronize to ensure a reliable ordering on role request // messages send and to ensure the list of connected switches is stable @@ -1483,6 +1496,9 @@ public class Controller implements IFloodlightProviderService, // ************** protected void updateAllInactiveSwitchInfo() { + if (role == Role.SLAVE) { + return; + } String controllerId = getControllerId(); String[] switchColumns = { SWITCH_DATAPATH_ID, SWITCH_CONTROLLER_ID, @@ -1540,6 +1556,9 @@ public class Controller implements IFloodlightProviderService, } protected void updateActiveSwitchInfo(IOFSwitch sw) { + if (role == Role.SLAVE) { + return; + } // Obtain the row info for the switch Map<String, Object> switchInfo = new HashMap<String, Object>(); String datapathIdString = sw.getStringId(); @@ -1584,6 +1603,9 @@ public class Controller implements IFloodlightProviderService, } protected void updateInactiveSwitchInfo(IOFSwitch sw) { + if (role == Role.SLAVE) { + return; + } log.debug("Update DB with inactiveSW {}", sw); // Update the controller info in the storage source to be inactive Map<String, Object> switchInfo = new HashMap<String, Object>(); @@ -1595,6 +1617,9 @@ public class Controller implements IFloodlightProviderService, } protected void updatePortInfo(IOFSwitch sw, OFPhysicalPort port) { + if (role == Role.SLAVE) { + return; + } String datapathIdString = sw.getStringId(); Map<String, Object> portInfo = new HashMap<String, Object>(); int portNumber = U16.f(port.getPortNumber()); @@ -1668,6 +1693,9 @@ public class Controller implements IFloodlightProviderService, } protected void removePortInfo(IOFSwitch sw, short portNumber) { + if (role == Role.SLAVE) { + return; + } String datapathIdString = sw.getStringId(); String id = datapathIdString + "|" + portNumber; storageSource.deleteRowAsync(PORT_TABLE_NAME, id); diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/OFMatchJSONSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/OFMatchJSONSerializer.java deleted file mode 100644 index 98048b1a59eb21ff5ad8627a82c73ecf8beb8564..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/OFMatchJSONSerializer.java +++ /dev/null @@ -1,91 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ - -package net.floodlightcontroller.core.web.serializers; - -import java.io.IOException; - -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonProcessingException; -import org.codehaus.jackson.map.JsonSerializer; -import org.codehaus.jackson.map.SerializerProvider; -import org.openflow.protocol.OFMatch; -import org.openflow.util.HexString; - -public class OFMatchJSONSerializer extends JsonSerializer<OFMatch> { - - /** - * Converts an IP in a 32 bit integer to a dotted-decimal string - * @param i The IP address in a 32 bit integer - * @return An IP address string in dotted-decimal - */ - private String intToIp(int i) { - return ((i >> 24 ) & 0xFF) + "." + - ((i >> 16 ) & 0xFF) + "." + - ((i >> 8 ) & 0xFF) + "." + - ( i & 0xFF); - } - - /** - * Performs the serialization of a OFMatch object - */ - @Override - public void serialize(OFMatch match, JsonGenerator jGen, - SerializerProvider serializer) - throws IOException, JsonProcessingException { - jGen.writeStartObject(); - jGen.writeStringField("dataLayerDestination", - HexString.toHexString(match.getDataLayerDestination())); - jGen.writeStringField("dataLayerSource", - HexString.toHexString(match.getDataLayerSource())); - String dataType = Integer.toHexString(match.getDataLayerType()); - while (dataType.length() < 4) { - dataType = "0".concat(dataType); - } - jGen.writeStringField("dataLayerType", "0x" + dataType); - jGen.writeNumberField("dataLayerVirtualLan", - match.getDataLayerVirtualLan()); - jGen.writeNumberField("dataLayerVirtualLanPriorityCodePoint", - match.getDataLayerVirtualLanPriorityCodePoint()); - jGen.writeNumberField("inputPort", match.getInputPort()); - jGen.writeStringField("networkDestination", - intToIp(match.getNetworkDestination())); - jGen.writeNumberField("networkDestinationMaskLen", - match.getNetworkDestinationMaskLen()); - jGen.writeNumberField("networkProtocol", match.getNetworkProtocol()); - jGen.writeStringField("networkSource", - intToIp(match.getNetworkSource())); - jGen.writeNumberField("networkSourceMaskLen", - match.getNetworkSourceMaskLen()); - jGen.writeNumberField("networkTypeOfService", - match.getNetworkTypeOfService()); - jGen.writeNumberField("transportDestination", - match.getTransportDestination()); - jGen.writeNumberField("transportSource", - match.getTransportSource()); - jGen.writeNumberField("wildcards", match.getWildcards()); - jGen.writeEndObject(); - } - - /** - * Tells SimpleModule that we are the serializer for OFMatch - */ - @Override - public Class<OFMatch> handledType() { - return OFMatch.class; - } -} diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java index dd54638212c9fdc8dcb746375a7cb936ecf1f25c..95969f8c859299fabd53d5a0d917db3ff90230fe 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDevice.java @@ -73,6 +73,14 @@ public interface IDevice { * @return an array containing all unique attachment points for the device */ public SwitchPort[] getAttachmentPoints(boolean includeError); + + /** + * Returns all unique VLAN IDs for the device that were observed on + * the given switch port + * @param swp the switch port to query + * @return an array containing the unique VLAN IDs + */ + public Short[] getSwitchPortVlanIds(SwitchPort swp); /** * Get the most recent timestamp for this device diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index dc7c0da7bf897cccbfc6e09a21762a3fddc25358..333777e731ac505dae9f31b3d886dce9de017cca 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -33,6 +33,7 @@ import net.floodlightcontroller.devicemanager.IDevice; import net.floodlightcontroller.devicemanager.IEntityClass; import net.floodlightcontroller.devicemanager.SwitchPort; import static net.floodlightcontroller.devicemanager.SwitchPort.ErrorStatus.*; +import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.topology.ITopologyService; /** @@ -342,6 +343,21 @@ public class Device implements IDevice { return vals.toArray(new SwitchPort[vals.size()]); } + + @Override + public Short[] getSwitchPortVlanIds(SwitchPort swp) { + TreeSet<Short> vals = new TreeSet<Short>(); + for (Entity e : entities) { + if (e.switchDPID == swp.getSwitchDPID() + && e.switchPort == swp.getPort()) { + if (e.getVlan() == null) + vals.add(Ethernet.VLAN_UNTAGGED); + else + vals.add(e.getVlan()); + } + } + return vals.toArray(new Short[vals.size()]); + } @Override public Date getLastSeen() { diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java index 4ac12eba8cfeb1c3a4c1d1cf16ccb70b85f01ca0..8496f0a8e4f9a5e4b670969599e98f04adacb218 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Entity.java @@ -213,7 +213,7 @@ public class Entity implements Comparable<Entity> { @Override public String toString() { - return "Entity [macAddress=" + HexString.toHexString(macAddress) + return "Entity [macAddress=" + HexString.toHexString(macAddress, 6) + ", ipv4Address=" + IPv4.fromIPv4Address(ipv4Address==null ? 0 : ipv4Address.intValue()) + ", vlan=" + vlan + ", switchDPID=" + switchDPID + ", switchPort=" + switchPort + "]"; diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java index a251bd3e7df26aaf45fe5bc6b40b41d16adff714..fa3fd4c88c58de6823a1997a30cf3b02429507b1 100644 --- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java +++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java @@ -291,11 +291,11 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule { @Override public void init(FloodlightModuleContext context) throws FloodlightModuleException { - this.setFloodlightProvider(context.getServiceImpl(IFloodlightProviderService.class)); - this.setDeviceManager(context.getServiceImpl(IDeviceService.class)); - this.setRoutingEngine(context.getServiceImpl(IRoutingService.class)); - this.setTopology(context.getServiceImpl(ITopologyService.class)); - this.setCounterStore(context.getServiceImpl(ICounterStoreService.class)); + this.floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); + this.deviceManager = context.getServiceImpl(IDeviceService.class); + this.routingEngine = context.getServiceImpl(IRoutingService.class); + this.topology = context.getServiceImpl(ITopologyService.class); + this.counterStore = context.getServiceImpl(ICounterStoreService.class); } @Override diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java index 08f7c50cd221eb577ac1177871f53871cb17a356..af731cea76b10533264a7eaa2a9661616f897e95 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java @@ -103,6 +103,29 @@ public interface ILinkDiscovery { }; public enum LinkType { - INVALID_LINK, DIRECT_LINK, MULTIHOP_LINK, TUNNEL + INVALID_LINK { + @Override + public String toString() { + return "invalid"; + } + }, + DIRECT_LINK{ + @Override + public String toString() { + return "internal"; + } + }, + MULTIHOP_LINK { + @Override + public String toString() { + return "external"; + } + }, + TUNNEL { + @Override + public String toString() { + return "tunnel"; + } + } }; } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java index b12be9379d67a4bd06ec523fd43a42db62ff90ca..9c0dd1a0a405f0b010ee0b9b80d3301d28c7ca4f 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java @@ -116,7 +116,7 @@ public class LinkInfo { result = prime * result + ((firstSeenTime == null) ? 0 : firstSeenTime.hashCode()); result = prime * result + ((lastLldpReceivedTime == null) ? 0 : lastLldpReceivedTime.hashCode()); result = prime * result + ((lastBddpReceivedTime == null) ? 0 : lastBddpReceivedTime.hashCode()); - result = prime * result + ((srcPortState == null) ? 0 : lastLldpReceivedTime.hashCode()); + result = prime * result + ((srcPortState == null) ? 0 : srcPortState.hashCode()); result = prime * result + ((dstPortState == null) ? 0 : dstPortState.hashCode()); return result; } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 9a3441c0e36e8217c1360528e2ade81e08ca5ba3..8c3cde18679295eb048fe513cc4c291d52ffc4ba 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -271,6 +271,30 @@ IFloodlightModule, IInfoProvider, IHAListener { return ILinkDiscovery.LinkType.INVALID_LINK; } + private void doUpdatesThread() throws InterruptedException { + do { + LDUpdate update = updates.take(); + + 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(update); + } + } + catch (Exception e) { + log.error("Error in link discovery updates loop", e); + } + } + } while (updates.peek() != null); + } + private boolean isLLDPSuppressed(long sw, short portNumber) { return this.suppressLLDPs.contains(new NodePortTuple(sw, portNumber)); } @@ -602,17 +626,23 @@ IFloodlightModule, IInfoProvider, IHAListener { } if (!remoteSwitch.portEnabled(remotePort)) { - log.debug("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort); + if (log.isTraceEnabled()) { + log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort); + } return Command.STOP; } if (suppressLLDPs.contains(new NodePortTuple(remoteSwitch.getId(), remotePort))) { - log.debug("Ignoring link with suppressed src port: switch {} port {}", + if (log.isTraceEnabled()) { + log.trace("Ignoring link with suppressed src port: switch {} port {}", remoteSwitch, remotePort); + } return Command.STOP; } if (!iofSwitch.portEnabled(pi.getInPort())) { - log.debug("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort()); + if (log.isTraceEnabled()) { + log.trace("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort()); + } return Command.STOP; } @@ -805,8 +835,8 @@ IFloodlightModule, IInfoProvider, IHAListener { if (linkChanged) { updateOperation = UpdateOperation.ADD_OR_UPDATE; - if (log.isDebugEnabled()) { - log.debug("Updated link {}", lt); + if (log.isTraceEnabled()) { + log.trace("Updated link {}", lt); } // Add to event history evHistTopoLink(lt.getSrc(), @@ -890,8 +920,8 @@ IFloodlightModule, IInfoProvider, IHAListener { removeLinkFromStorage(lt); - if (log.isDebugEnabled()) { - log.debug("Deleted link {}", lt); + if (log.isTraceEnabled()) { + log.trace("Deleted link {}", lt); } } } finally { @@ -911,8 +941,8 @@ IFloodlightModule, IInfoProvider, IHAListener { IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); if (iofSwitch == null) return Command.CONTINUE; - if (log.isDebugEnabled()) { - log.debug("handlePortStatus: Switch {} port #{} reason {}; " + + if (log.isTraceEnabled()) { + log.trace("handlePortStatus: Switch {} port #{} reason {}; " + "config is {} state is {}", new Object[] {iofSwitch.getStringId(), ps.getDesc().getPortNumber(), @@ -979,8 +1009,8 @@ IFloodlightModule, IInfoProvider, IHAListener { } if (!linkDeleted && !linkInfoChanged){ - if (log.isDebugEnabled()) { - log.debug("handlePortStatus: Switch {} port #{} reason {};"+ + if (log.isTraceEnabled()) { + log.trace("handlePortStatus: Switch {} port #{} reason {};"+ " no links to update/remove", new Object[] {HexString.toHexString(sw), ps.getDesc().getPortNumber(), @@ -1037,8 +1067,8 @@ IFloodlightModule, IInfoProvider, IHAListener { lock.writeLock().lock(); try { if (switchLinks.containsKey(sw)) { - if (log.isDebugEnabled()) { - log.debug("Handle switchRemoved. Switch {}; removing links {}", + if (log.isTraceEnabled()) { + log.trace("Handle switchRemoved. Switch {}; removing links {}", HexString.toHexString(sw), switchLinks.get(sw)); } // add all tuples with an endpoint on this switch to erase list @@ -1058,8 +1088,8 @@ IFloodlightModule, IInfoProvider, IHAListener { protected void deleteLinksOnPort(NodePortTuple npt, String reason) { List<Link> eraseList = new ArrayList<Link>(); if (this.portLinks.containsKey(npt)) { - if (log.isDebugEnabled()) { - log.debug("handlePortStatus: Switch {} port #{} " + + if (log.isTraceEnabled()) { + log.trace("handlePortStatus: Switch {} port #{} " + "removing links {}", new Object[] {HexString.toHexString(npt.getNodeId()), npt.getPortId(), @@ -1438,8 +1468,8 @@ IFloodlightModule, IInfoProvider, IHAListener { updated_switches.add(sw); } } else { - if (log.isDebugEnabled()) { - log.debug("Update for switch which has no entry in switch " + + if (log.isTraceEnabled()) { + log.trace("Update for switch which has no entry in switch " + "list (dpid={}), a delete action.", (String)key); } } @@ -1449,15 +1479,15 @@ IFloodlightModule, IInfoProvider, IHAListener { // 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.isDebugEnabled()) { - log.debug("SWITCH_IS_CORE_SWITCH set to False for {}", sw); + if (log.isTraceEnabled()) { + log.trace("SWITCH_IS_CORE_SWITCH set to False for {}", sw); } updates.add(new LDUpdate(sw.getId(), SwitchType.BASIC_SWITCH)); } else { sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, new Boolean(true)); - if (log.isDebugEnabled()) { - log.debug("SWITCH_IS_CORE_SWITCH set to True for {}", sw); + if (log.isTraceEnabled()) { + log.trace("SWITCH_IS_CORE_SWITCH set to True for {}", sw); } updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH)); } @@ -1568,6 +1598,19 @@ IFloodlightModule, IInfoProvider, IHAListener { } }); + updatesThread = new Thread(new Runnable () { + @Override + public void run() { + while (true) { + try { + doUpdatesThread(); + } catch (InterruptedException e) { + return; + } + } + }}, "Topology Updates"); + updatesThread.start(); + // Register for the OpenFlow messages we want to receive floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this); @@ -1680,15 +1723,19 @@ IFloodlightModule, IInfoProvider, IHAListener { case MASTER: if (oldRole == Role.SLAVE) { clearAllLinks(); - log.debug("Sending LLDPs " + - "to HA change from SLAVE->MASTER"); + if (log.isTraceEnabled()) { + log.trace("Sending LLDPs " + + "to HA change from SLAVE->MASTER"); + } clearAllLinks(); discoverLinks(); } break; case SLAVE: - log.debug("Clearing links due to " + - "HA change to SLAVE"); + if (log.isTraceEnabled()) { + log.trace("Clearing links due to " + + "HA change to SLAVE"); + } switchLinks.clear(); links.clear(); portLinks.clear(); diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinkWithType.java b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinkWithType.java new file mode 100644 index 0000000000000000000000000000000000000000..514cfe4f62acd329e772879c38296f9c5f7b1ff4 --- /dev/null +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinkWithType.java @@ -0,0 +1,56 @@ +package net.floodlightcontroller.linkdiscovery.web; + +import java.io.IOException; + +import org.codehaus.jackson.JsonGenerator; +import org.codehaus.jackson.JsonProcessingException; +import org.codehaus.jackson.map.JsonSerializer; +import org.codehaus.jackson.map.SerializerProvider; +import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.openflow.util.HexString; + +import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType; +import net.floodlightcontroller.routing.Link; + +/** + * This class is both the datastructure and the serializer + * for a link with the corresponding type of link. + * @author alexreimers + */ +@JsonSerialize(using=LinkWithType.class) +public class LinkWithType extends JsonSerializer<LinkWithType> { + public long srcSwDpid; + public short srcPort; + public long dstSwDpid; + public short dstPort; + public LinkType type; + + // Do NOT delete this, it's required for the serializer + public LinkWithType() {} + + public LinkWithType(Link link, LinkType type) { + this.srcSwDpid = link.getSrc(); + this.srcPort = link.getSrcPort(); + this.dstSwDpid = link.getDst(); + this.dstPort = link.getDstPort(); + this.type = type; + } + + @Override + public void serialize(LinkWithType lwt, JsonGenerator jgen, SerializerProvider arg2) + throws IOException, JsonProcessingException { + // You ****MUST*** use lwt for the fields as it's actually a different object. + jgen.writeStartObject(); + jgen.writeStringField("src-switch", HexString.toHexString(lwt.srcSwDpid)); + jgen.writeNumberField("src-port", lwt.srcPort); + jgen.writeStringField("dst-switch", HexString.toHexString(lwt.dstSwDpid)); + jgen.writeNumberField("dst-port", lwt.dstPort); + jgen.writeStringField("type", lwt.type.toString()); + jgen.writeEndObject(); + } + + @Override + public Class<LinkWithType> handledType() { + return LinkWithType.class; + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java index eda84c591bc3645c42e0643861b044d0097df7a9..8fe1ff046f192cd0bcded0792a0bbf3a7030a737 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java @@ -5,7 +5,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService; import net.floodlightcontroller.linkdiscovery.LinkInfo; import net.floodlightcontroller.routing.Link; @@ -15,26 +14,8 @@ import org.restlet.resource.ServerResource; public class LinksResource extends ServerResource { - public class LinkWithType { - long src; - short srcPort; - long dst; - short dstPort; - String type; - - public LinkWithType(Link link, String type) { - this.src = link.getSrc(); - this.srcPort = link.getSrcPort(); - this.dst = link.getDst(); - this.dstPort = link.getDstPort(); - this.type = type; - } - }; - @Get("json") public Set<LinkWithType> retrieve() { - String str; - ILinkDiscoveryService ld = (ILinkDiscoveryService)getContext().getAttributes(). get(ILinkDiscoveryService.class.getCanonicalName()); Map<Link, LinkInfo> links = new HashMap<Link, LinkInfo>(); @@ -44,17 +25,7 @@ public class LinksResource extends ServerResource { links.putAll(ld.getLinks()); for (Link link: links.keySet()) { LinkInfo info = links.get(link); - LinkType type = ld.getLinkType(info); - - if (type == LinkType.DIRECT_LINK) - str = "internal"; - else if (type == LinkType.MULTIHOP_LINK) - str = "external"; - else if (type == LinkType.TUNNEL) - str = "tunnel"; - else str = "invalid"; - - LinkWithType lwt = new LinkWithType(link, str); + LinkWithType lwt = new LinkWithType(link, ld.getLinkType(info)); returnLinkSet.add(lwt); } } diff --git a/src/main/java/net/floodlightcontroller/packet/BasePacket.java b/src/main/java/net/floodlightcontroller/packet/BasePacket.java index d8729ef0dba6856ea358e5b141c42ce0ecc47bdf..6df676d6bf5eb3e74eb0b14e9d3ea6189636fd6b 100644 --- a/src/main/java/net/floodlightcontroller/packet/BasePacket.java +++ b/src/main/java/net/floodlightcontroller/packet/BasePacket.java @@ -17,6 +17,7 @@ package net.floodlightcontroller.packet; + /** * * @author David Erickson (daviderickson@cs.stanford.edu) @@ -85,4 +86,21 @@ public abstract class BasePacket implements IPacket { return false; return true; } -} + + @Override + public Object clone() { + IPacket pkt; + try { + pkt = this.getClass().newInstance(); + } catch (Exception e) { + throw new RuntimeException("Could not clone packet"); + } + // TODO: we are using serialize()/deserialize() to perform the + // cloning. Not the most efficient way but simple. We can revisit + // if we hit performance problems. + byte[] data = this.serialize(); + pkt.deserialize(this.serialize(), 0, data.length); + pkt.setParent(this.parent); + return pkt; + } +} \ No newline at end of file diff --git a/src/main/java/net/floodlightcontroller/packet/IPacket.java b/src/main/java/net/floodlightcontroller/packet/IPacket.java index 8f783e31baa720229e0efeaf3b6f8bf5c8247114..094cfc700a10a4aab4d1ca7531333aed3309ace9 100644 --- a/src/main/java/net/floodlightcontroller/packet/IPacket.java +++ b/src/main/java/net/floodlightcontroller/packet/IPacket.java @@ -63,4 +63,10 @@ public interface IPacket { * @return the deserialized data */ public IPacket deserialize(byte[] data, int offset, int length); + + /** Clone this packet and its payload packet but not its parent. + * + * @return + */ + public Object clone(); } diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index 445956a4d01cc8dd0bd40b1276f5275859f0c01d..d56bbb67f06f5bb7152e9d610a275efd207d1295 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -20,9 +20,9 @@ package net.floodlightcontroller.routing; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; -import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -35,6 +35,7 @@ import net.floodlightcontroller.devicemanager.IDeviceListener; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.IRoutingDecision; import net.floodlightcontroller.routing.Route; @@ -178,6 +179,7 @@ public abstract class ForwardingBase implements (OFFlowMod) floodlightProvider.getOFMessageFactory() .getMessage(OFType.FLOW_MOD); OFActionOutput action = new OFActionOutput(); + action.setMaxLength((short)0xffff); List<OFAction> actions = new ArrayList<OFAction>(); actions.add(action); @@ -208,9 +210,6 @@ public abstract class ForwardingBase implements // set buffer id if it is the source switch if (1 == indx) { - //fm.setMatch(match); - fm.setBufferId(bufferId); - //fm.setMatch(wildcard(match, sw, wildcard_hints)); // Set the flag to request flow-mod removal notifications only for the // source switch. The removal message is used to maintain the flow // cache. Don't set the flag for ARP messages - TODO generalize check @@ -244,6 +243,8 @@ public abstract class ForwardingBase implements // Push the packet out the source switch if (sw.getId() == pinSwitch) { + // TODO: Instead of doing a packetOut here we could also + // send a flowMod with bufferId set.... pushPacket(sw, match, pi, outPort, cntx); srcSwitchIncluded = true; } @@ -268,6 +269,72 @@ public abstract class ForwardingBase implements } return match.clone(); } + + /** + * Pushes a packet-out to a switch. If bufferId != BUFFER_ID_NONE we + * assume that the packetOut switch is the same as the packetIn switch + * and we will use the bufferId + * Caller needs to make sure that inPort and outPort differs + * @param packet packet data to send + * @param sw switch from which packet-out is sent + * @param bufferId bufferId + * @param inPort input port + * @param outPort output port + * @param cntx context of the packet + */ + public void pushPacket(IPacket packet, + IOFSwitch sw, + int bufferId, + short inPort, + short outPort, + FloodlightContext cntx) { + + + if (log.isTraceEnabled()) { + log.trace("PacketOut srcSwitch={} inPort={} outPort={}", + new Object[] {sw, inPort, outPort}); + } + + OFPacketOut po = + (OFPacketOut) floodlightProvider.getOFMessageFactory() + .getMessage(OFType.PACKET_OUT); + + // set actions + List<OFAction> actions = new ArrayList<OFAction>(); + actions.add(new OFActionOutput(outPort, (short) 0xffff)); + + po.setActions(actions) + .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); + short poLength = + (short) (po.getActionsLength() + OFPacketOut.MINIMUM_LENGTH); + + // set buffer_id, in_port + po.setBufferId(bufferId); + po.setInPort(inPort); + + // set data - only if buffer_id == -1 + if (po.getBufferId() == OFPacketOut.BUFFER_ID_NONE) { + if (packet == null) { + log.error("BufferId is set but no packet data is null. " + + "Cannot send packetOut. " + + "srcSwitch={} inPort={} outPort={}", + new Object[] {sw, inPort, outPort}); + return; + } + byte[] packetData = packet.serialize(); + poLength += packetData.length; + po.setPacketData(packetData); + } + + po.setLength(poLength); + + try { + counterStore.updatePktOutFMCounterStore(sw, po); + sw.write(po, cntx); + } catch (IOException e) { + log.error("Failure writing packet out", e); + } + } /** * Pushes a packet-out to a switch. The assumption here is that @@ -311,7 +378,7 @@ public abstract class ForwardingBase implements // set actions List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(new OFActionOutput(outport, (short) 0)); + actions.add(new OFActionOutput(outport, (short) 0xffff)); po.setActions(actions) .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); @@ -339,20 +406,20 @@ public abstract class ForwardingBase implements } } + /** * Write packetout message to sw with output actions to one or more * output ports with inPort/outPorts passed in. - * Note that the packet in could be from a different switch. - * @param pi + * @param packetData * @param sw * @param inPort * @param ports * @param cntx */ - public void PacketOutMultiPort(OFPacketIn pi, + public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, short inPort, - HashSet<Integer> outPorts, + Set<Integer> outPorts, FloodlightContext cntx) { //setting actions List<OFAction> actions = new ArrayList<OFAction>(); @@ -379,7 +446,6 @@ public abstract class ForwardingBase implements // data (note buffer_id is always BUFFER_ID_NONE) and length short poLength = (short)(po.getActionsLength() + OFPacketOut.MINIMUM_LENGTH); - byte[] packetData = pi.getPacketData(); poLength += packetData.length; po.setPacketData(packetData); po.setLength(poLength); @@ -388,8 +454,8 @@ public abstract class ForwardingBase implements counterStore.updatePktOutFMCounterStore(sw, po); if (log.isTraceEnabled()) { log.trace("write broadcast packet on switch-id={} " + - "interaces={} packet-in={} packet-out={}", - new Object[] {sw.getId(), outPorts, pi, po}); + "interfaces={} packet-out={}", + new Object[] {sw.getId(), outPorts, po}); } sw.write(po, cntx); @@ -397,6 +463,32 @@ public abstract class ForwardingBase implements log.error("Failure writing packet out", e); } } + + /** + * @see packetOutMultiPort + * Accepts a PacketIn instead of raw packet data. Note that the inPort + * and switch can be different than the packet in switch/port + */ + public void packetOutMultiPort(OFPacketIn pi, + IOFSwitch sw, + short inPort, + Set<Integer> outPorts, + FloodlightContext cntx) { + packetOutMultiPort(pi.getPacketData(), sw, inPort, outPorts, cntx); + } + + /** + * @see packetOutMultiPort + * Accepts an IPacket instead of raw packet data. Note that the inPort + * and switch can be different than the packet in switch/port + */ + public void packetOutMultiPort(IPacket packet, + IOFSwitch sw, + short inPort, + Set<Integer> outPorts, + FloodlightContext cntx) { + packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx); + } protected boolean isInBroadcastCache(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) { @@ -486,40 +578,6 @@ public abstract class ForwardingBase implements } - /** - * @param floodlightProvider the floodlightProvider to set - */ - public void setFloodlightProvider(IFloodlightProviderService floodlightProvider) { - this.floodlightProvider = floodlightProvider; - } - - /** - * @param routingEngine the routingEngine to set - */ - public void setRoutingEngine(IRoutingService routingEngine) { - this.routingEngine = routingEngine; - } - - /** - * @param deviceManager - * the deviceManager to set - */ - public void setDeviceManager(IDeviceService deviceManager) { - this.deviceManager = deviceManager; - } - - /** - * @param topology - * the topology to set - */ - public void setTopology(ITopologyService topology) { - this.topology = topology; - } - - public void setCounterStore(ICounterStoreService counterStore) { - this.counterStore = counterStore; - } - @Override public void deviceAdded(IDevice device) { // NOOP diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index 462612dfb820f9f966a64f0a1deb7f00cf0e3b10..fae8fd2a071ca8b1881bd942f6ca6a36de051ef1 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -616,7 +616,7 @@ public class TopologyManager implements floodlightProvider.addHAListener(this); addRestletRoutable(); } - + protected void addRestletRoutable() { restApi.addRestletRoutable(new TopologyWebRoutable()); } @@ -624,31 +624,35 @@ public class TopologyManager implements // **************** // Internal methods // **************** - protected Command dropFilter(IOFSwitch sw, OFPacketIn pi, + /** + * If the packet-in switch port is disabled for all data traffic, then + * the packet will be dropped. Otherwise, the packet will follow the + * normal processing chain. + * @param sw + * @param pi + * @param cntx + * @return + */ + protected Command dropFilter(long sw, OFPacketIn pi, FloodlightContext cntx) { Command result = Command.CONTINUE; - Ethernet eth = - IFloodlightProviderService.bcStore. - get(cntx,IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + short port = pi.getInPort(); - if (isAllowed(sw.getId(), pi.getInPort()) == false) { - if (eth.getEtherType() == Ethernet.TYPE_BDDP || - (eth.isBroadcast() == false && eth.isMulticast() == false)) { - result = Command.CONTINUE; - } else { - if (log.isTraceEnabled()) { - log.trace("Ignoring packet because of topology " + - "restriction on switch={}, port={}", - new Object[] {sw.getStringId(), - pi.getInPort()}); - } + // If the input port is not allowed for data traffic, drop everything. + // BDDP packets will not reach this stage. + if (isAllowed(sw, port) == false) { + if (log.isTraceEnabled()) { + log.trace("Ignoring packet because of topology " + + "restriction on switch={}, port={}", sw, port); 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. @@ -723,9 +727,10 @@ public class TopologyManager implements TopologyInstance ti = getCurrentInstance(false); Set<Long> switches = ti.getSwitchesInOpenflowDomain(pinSwitch); - - if (switches == null) // this implies that there are no links connected to the switches + + if (switches == null) { + // indicates no links are connected to the switches switches = new HashSet<Long>(); switches.add(pinSwitch); } @@ -774,9 +779,7 @@ public class TopologyManager implements if (eth.getEtherType() == Ethernet.TYPE_BDDP) { doFloodBDDP(sw.getId(), pi, cntx); } else { - // if the packet is BDDP, then send flood it on all the external - // switch ports in the same openflow domain. - return dropFilter(sw, pi, cntx); + return dropFilter(sw.getId(), pi, cntx); } return Command.STOP; } @@ -823,7 +826,7 @@ public class TopologyManager implements update.getDst(), update.getDstPort()); updateApplied = true; } - + if (updateApplied) { appliedUpdates.add(newUpdate); } diff --git a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule index af007737c2308aaf1d7616c0cd22651623229a5d..20b677aef010bab17c2de08801c22d68e9063c25 100644 --- a/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule +++ b/src/main/resources/META-INF/services/net.floodlightcontroller.core.module.IFloodlightModule @@ -18,4 +18,4 @@ net.floodlightcontroller.counter.NullCounterStore net.floodlightcontroller.threadpool.ThreadPool net.floodlightcontroller.ui.web.StaticWebRoutable net.floodlightcontroller.virtualnetwork.VirtualNetworkFilter -net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier \ No newline at end of file +net.floodlightcontroller.devicemanager.internal.DefaultEntityClassifier diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index f0bc0cdbde2db152c04be08504a4019ebeea6001..2d86734d465e6d436e55b8a1f049faa02522fd6b 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -1380,4 +1380,30 @@ 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 + public void testGetSwitchPortVlanId() { + Entity entity1 = new Entity(1L, (short)1, null, 10L, 1, new Date()); + Entity entity2 = new Entity(1L, null, null, 10L, 1, new Date()); + Entity entity3 = new Entity(1L, (short)3, null, 1L, 1, new Date()); + Entity entity4 = new Entity(1L, (short)42, null, 1L, 1, new Date()); + Entity[] entities = new Entity[] { entity1, entity2, + entity3, entity4 + }; + Device d = new Device(null,1L, Arrays.asList(entities), null); + SwitchPort swp1x1 = new SwitchPort(1L, 1); + SwitchPort swp1x2 = new SwitchPort(1L, 2); + SwitchPort swp2x1 = new SwitchPort(2L, 1); + SwitchPort swp10x1 = new SwitchPort(10L, 1); + assertArrayEquals(new Short[] { -1, 1}, + d.getSwitchPortVlanIds(swp10x1)); + assertArrayEquals(new Short[] { 3, 42}, + d.getSwitchPortVlanIds(swp1x1)); + assertArrayEquals(new Short[0], + d.getSwitchPortVlanIds(swp1x2)); + assertArrayEquals(new Short[0], + d.getSwitchPortVlanIds(swp2x1)); + } } diff --git a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java index 0d850a789fccbb818ffbe65c9a587e163c321198..feafc185b8471cd09e6f771b1cb883ecd3ced478 100644 --- a/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java +++ b/src/test/java/net/floodlightcontroller/forwarding/ForwardingTest.java @@ -215,7 +215,7 @@ public class ForwardingTest extends FloodlightTestCase { packetOut.setBufferId(this.packetIn.getBufferId()) .setInPort(this.packetIn.getInPort()); List<OFAction> poactions = new ArrayList<OFAction>(); - poactions.add(new OFActionOutput((short) 3, (short) 0)); + poactions.add(new OFActionOutput((short) 3, (short) 0xffff)); packetOut.setActions(poactions) .setActionsLength((short) OFActionOutput.MINIMUM_LENGTH) .setPacketData(testPacketSerialized) @@ -274,7 +274,7 @@ public class ForwardingTest extends FloodlightTestCase { // Expected Flow-mods OFMatch match = new OFMatch(); match.loadFromPacket(testPacketSerialized, (short) 1); - OFActionOutput action = new OFActionOutput((short)3, (short)0); + OFActionOutput action = new OFActionOutput((short)3, (short)0xffff); List<OFAction> actions = new ArrayList<OFAction>(); actions.add(action); @@ -310,9 +310,9 @@ public class ForwardingTest extends FloodlightTestCase { for (OFMessage m: msglist) { if (m instanceof OFFlowMod) - assertTrue(m.equals(fm1)); + assertEquals(fm1, m); else if (m instanceof OFPacketOut) - assertTrue(m.equals(packetOut)); + assertEquals(packetOut, m); } OFMessage m = wc2.getValue(); @@ -356,7 +356,7 @@ public class ForwardingTest extends FloodlightTestCase { // Expected Flow-mods OFMatch match = new OFMatch(); match.loadFromPacket(testPacketSerialized, (short) 1); - OFActionOutput action = new OFActionOutput((short)3, (short)0); + OFActionOutput action = new OFActionOutput((short)3, (short)0xffff); List<OFAction> actions = new ArrayList<OFAction>(); actions.add(action); diff --git a/src/test/java/net/floodlightcontroller/packet/PacketTest.java b/src/test/java/net/floodlightcontroller/packet/PacketTest.java new file mode 100644 index 0000000000000000000000000000000000000000..67bed71632c5b8163b57219c5488c9b8b007ad0b --- /dev/null +++ b/src/test/java/net/floodlightcontroller/packet/PacketTest.java @@ -0,0 +1,126 @@ +package net.floodlightcontroller.packet; + +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; + +public class PacketTest { + protected IPacket pkt1, pkt2, pkt3, pkt4; + protected IPacket dummyPkt; + protected IPacket[] packets; + + @Before + public void setUp() { + this.pkt1 = new Ethernet() + .setDestinationMACAddress("00:11:22:33:44:55") + .setSourceMACAddress("00:44:33:22:11:00") + .setEtherType(Ethernet.TYPE_IPv4) + .setPayload( + new IPv4() + .setTtl((byte) 128) + .setSourceAddress("192.168.1.1") + .setDestinationAddress("192.168.1.2") + .setPayload(new UDP() + .setSourcePort((short) 5000) + .setDestinationPort((short) 5001) + .setPayload(new Data(new byte[] {0x01})))); + + this.pkt2 = new Ethernet() + .setSourceMACAddress("00:44:33:22:11:01") + .setDestinationMACAddress("00:11:22:33:44:55") + .setEtherType(Ethernet.TYPE_ARP) + .setVlanID((short)5) + .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.1")) + .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) + .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); + + + this.pkt3 = 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.1")) + .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) + .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); + + this.pkt4 = new Ethernet() + .setDestinationMACAddress("FF:FF:FF:FF:FF:FF") + .setSourceMACAddress("00:11:33:55:77:01") + .setEtherType(Ethernet.TYPE_IPv4) + .setPayload( + new IPv4() + .setTtl((byte) 128) + .setSourceAddress("192.168.10.1") + .setDestinationAddress("192.168.255.255") + .setPayload(new UDP() + .setSourcePort((short) 5000) + .setDestinationPort((short) 5001) + .setPayload(new Data(new byte[] {0x01})))); + + this.dummyPkt = new IPv4() + .setTtl((byte) 32) + .setSourceAddress("1.2.3.4") + .setDestinationAddress("5.6.7.8"); + + this.packets = new IPacket[] { pkt1, pkt2, pkt3, pkt4 }; + } + + protected void doTestClone(IPacket pkt) { + if (pkt.getPayload() != null) + doTestClone(pkt.getPayload()); + IPacket newPkt = (IPacket)pkt.clone(); + assertSame(pkt.getClass(), newPkt.getClass()); + assertNotSame(pkt, newPkt); + assertSame(pkt.getParent(), newPkt.getParent()); + assertEquals(pkt, newPkt); + assertEquals(pkt.getPayload(), newPkt.getPayload()); + if (pkt.getPayload() != null) + assertNotSame(pkt.getPayload(), newPkt.getPayload()); + + if (pkt instanceof Ethernet) { + Ethernet eth = (Ethernet)pkt; + Ethernet newEth = (Ethernet)newPkt; + newEth.setDestinationMACAddress(new byte[] { 1,2,3,4,5,6}); + assertEquals(false, newEth.getDestinationMAC() + .equals(eth.getDestinationMAC())); + assertEquals(false, newPkt.equals(pkt)); + } + if (pkt instanceof ARP) { + ARP arp = (ARP)pkt; + ARP newArp = (ARP)newPkt; + newArp.setSenderProtocolAddress(new byte[] {1,2,3,4}); + assertEquals(false, newArp.getSenderProtocolAddress() + .equals(arp.getSenderProtocolAddress())); + assertEquals(false, newPkt.equals(pkt)); + } + + byte[] dummyData = dummyPkt.serialize().clone(); + newPkt = (IPacket)pkt.clone(); + newPkt.deserialize(dummyData, 0, dummyData.length); + assertEquals(false, newPkt.equals(pkt)); + } + + @Test + public void testClone() { + for (IPacket pkt: packets) { + doTestClone(pkt); + } + } + +}