From 8dc731c29a80828f7872d955c03fb51e1a79b2a2 Mon Sep 17 00:00:00 2001 From: kwanggithub <kwang@clemson.edu> Date: Sat, 2 Feb 2013 23:23:10 -0800 Subject: [PATCH] Squashed commit of the following: commit 8f1917bd34be3c81f4fc049b4736e4fbe3f1cd0e Author: Shudong Zhou <shudongzhou@gmail.com> Date: Thu Jan 31 00:59:16 2013 -0800 Fix singleton task initialization commit dd361d41dafca32cae1f2e8f6be0725633082dc5 Author: Shudong Zhou <shudongzhou@gmail.com> Date: Wed Jan 30 21:58:22 2013 -0800 Send HA role request to switch when master controller gets permission error commit c18d87699bda09c07707a36e026fb5a4e6301e0d Author: Shudong Zhou <shudongzhou@gmail.com> Date: Wed Jan 30 20:33:51 2013 -0800 Dampen frequency of HA role transitions commit ed7c1bd030ce6826510c52427ccea1f8631e09fa Author: Shudong Zhou <shudongzhou@gmail.com> Date: Wed Jan 30 20:04:43 2013 -0800 Update unit test for HA role change commit 32dc0b95b34a82902f1309aebbe469b4b713b356 Merge: 4963170 63a85a2 Author: abat <abat@bigswitch.com> Date: Wed Jan 30 16:27:28 2013 -0800 Merge into master from pull request #2414: lower a message level to trace commit 63a85a28be92ca5c14d826198a572c9e408b0343 Author: Kanzhe Jiang <kanzhe.jiang@bigswitch.com> Date: Wed Jan 30 14:55:52 2013 -0800 lower log level to info commit 49631705e7393452cd6825ae126e053c6ff3f3a1 Author: Gregor Maier <gregor.maier@bigswitch.com> Date: Wed Jan 30 10:10:07 2013 -0800 Adding debug logging to SwitchPortMatcher. Tweak log levels. We'll now log when SwitchPortMatcher can't find the switch or port for a packet its trying to match. commit ca75639f53067087be0b308e3aa72e07bf75d924 Author: Srinivasan Ramasubramanian <srini@bigswitch.com> Date: Tue Jan 29 12:20:13 2013 -0800 Bug fix: When tunnel links are identified, they should not be added to topology switchportlinks. Update unit tests. commit 3ee7da2f062834929212a568bf07cb462eb41979 Author: kwanggithub <kwang@clemson.edu> Date: Mon Jan 28 11:51:52 2013 -0800 refactor loadbalancer to not use StaticFlowEntries commit a0cac00f334970199ca2af9679d927d69fa539ab Author: Kanzhe Jiang <kanzhe.jiang@bigswitch.com> Date: Sun Jan 27 18:05:23 2013 -0800 notify listeners for device classification first, then listeners for reconcile. commit 5925b52aa2f0d7aae290dd22d30626bf81e879c3 Author: Srinivasan Ramasubramanian <srini@bigswitch.com> Date: Wed Jan 23 01:24:29 2013 -0800 Refactoring some methods to better support extensibility. commit ae577282bdc906262cf3fed44eabf685090ca60e Merge: 9f1613e 1d88cbf Author: abat <abat@bigswitch.com> Date: Mon Jan 21 17:22:21 2013 -0800 Merge into master from pull request #2255 commit 9f1613e936c5b9485a798486e19670b8dc2dd8eb Author: Gregor Maier <gregor.maier@bigswitch.com> Date: Sat Jan 19 23:25:48 2013 -0800 Don't use vlans from devices. Use explicitly specified VLAN. commit 1d88cbff4bc53f8936145abc8ca653fa0d58c9ce Author: Ronald Li <ronald.li@bigswitch.com> Date: Thu Jan 10 18:08:39 2013 -0800 --- .../core/internal/Controller.java | 50 +- .../devicemanager/IDeviceService.java | 24 +- .../devicemanager/internal/Device.java | 9 + .../internal/DeviceManagerImpl.java | 129 +++-- .../devicemanager/web/DeviceSerializer.java | 2 + .../flowcache/FCQueryObj.java | 6 +- .../linkdiscovery/ILinkDiscoveryService.java | 14 +- .../internal/LinkDiscoveryManager.java | 119 +++-- .../loadbalancer/LoadBalancer.java | 504 +++++++++++++++++- .../net/floodlightcontroller/packet/DHCP.java | 1 + .../net/floodlightcontroller/packet/LLDP.java | 5 +- .../routing/ForwardingBase.java | 29 +- .../topology/TopologyManager.java | 55 +- .../virtualnetwork/VirtualNetworkFilter.java | 2 +- .../core/internal/ControllerTest.java | 3 + .../internal/DeviceManagerImplTest.java | 15 +- .../internal/DeviceUniqueIndexTest.java | 6 +- .../devicemanager/test/MockDevice.java | 3 +- .../devicemanager/test/MockDeviceManager.java | 10 +- .../internal/LinkDiscoveryManagerTest.java | 2 +- 20 files changed, 840 insertions(+), 148 deletions(-) diff --git a/src/main/java/net/floodlightcontroller/core/internal/Controller.java b/src/main/java/net/floodlightcontroller/core/internal/Controller.java index 5f9cbd81d..73f3bed66 100644 --- a/src/main/java/net/floodlightcontroller/core/internal/Controller.java +++ b/src/main/java/net/floodlightcontroller/core/internal/Controller.java @@ -41,6 +41,8 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import net.floodlightcontroller.core.FloodlightContext; import net.floodlightcontroller.core.IFloodlightProviderService; @@ -57,6 +59,7 @@ import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.annotations.LogMessageDocs; import net.floodlightcontroller.core.internal.OFChannelState.HandshakeState; import net.floodlightcontroller.core.util.ListenerDispatcher; +import net.floodlightcontroller.core.util.SingletonTask; import net.floodlightcontroller.core.web.CoreWebRoutable; import net.floodlightcontroller.counter.ICounterStoreService; import net.floodlightcontroller.flowcache.IFlowCacheService; @@ -186,9 +189,12 @@ public class Controller implements IFloodlightProviderService, // this role and then notify, on a transition to master we first notify // and then set the role. We then use it to make sure we don't forward // OF messages while the modules are in slave role. - protected volatile Role notifiedRole; + // The pendingRole is a role change just received, but not sent out + // notifications yet. + protected Role pendingRole;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; @@ -289,8 +295,8 @@ public class Controller implements IFloodlightProviderService, } return; } - if (log.isTraceEnabled()) { - log.trace("Dispatching HA Role update newRole = {}, oldRole = {}", + if (log.isDebugEnabled()) { + log.debug("Dispatching HA Role update newRole = {}, oldRole = {}", newRole, oldRole); } // Set notified role to slave before notifying listeners. This @@ -382,19 +388,26 @@ public class Controller implements IFloodlightProviderService, public void setRole(Role role) { 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; + roleChangeDamper.reschedule(2000, TimeUnit.MILLISECONDS); + } + + protected void doSetRole() { // Need to synchronize to ensure a reliable ordering on role request // messages send and to ensure the list of connected switches is stable // RoleChanger will handle the actual sending of the message and // timeout handling // @see RoleChanger synchronized(roleChanger) { - if (role.equals(this.role)) { + if (pendingRole.equals(this.role)) { log.debug("Ignoring role change: role is already {}", role); return; } Role oldRole = this.role; - this.role = role; + this.role = pendingRole; log.debug("Submitting role change request to role {}", role); roleChanger.submitRequest(connectedSwitches, role); @@ -974,8 +987,22 @@ public class Controller implements IFloodlightProviderService, if (roleChanger.checkFirstPendingRoleRequestXid( sw, error.getXid())) { roleChanger.deliverRoleRequestError(sw, error); - } - else { + } else if (error.getErrorCode() == + OFErrorType.OFPET_BAD_REQUEST.getValue() && + error.getErrorType() == + OFBadRequestCode.OFPBRC_EPERM.ordinal() && + role.equals(Role.MASTER)) { + // We are the master and the switch returned permission + // error. Send a role change request in case switch set + // the master to someone else. + // Only send if there are no pending requests. + synchronized(roleChanger) { + if (roleChanger.pendingRequestMap.get(sw) == null) { + log.info("Tell switch {} who is the master", sw); + roleChanger.submitRequest(Collections.singleton(sw), role); + } + } + } else { logError(sw, error); // allow registered listeners to receive error messages @@ -1809,6 +1836,15 @@ public class Controller implements IFloodlightProviderService, // Add our REST API restApi.addRestletRoutable(new CoreWebRoutable()); + + // Start role change task + ScheduledExecutorService ses = threadPool.getScheduledExecutor(); + roleChangeDamper = new SingletonTask(ses, new Runnable() { + @Override + public void run() { + doSetRole(); + } + }); } @Override diff --git a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java index e161825a6..1164163f5 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/IDeviceService.java @@ -30,6 +30,23 @@ import net.floodlightcontroller.core.module.IFloodlightService; * from the {@link FloodlightContext} rather than from {@link IDeviceManager}. */ public interface IDeviceService extends IFloodlightService { + + /** + * IDeviceListeners do one of two actions on devices in response to the device events: + * 1. Reclassify device + * 2. Reconcile flows for the device. + * + * We must make sure all device classification are done before flow reconciliation. + * So all listeners are grouped into two groups based on their types. + * Reclassification group is notified first, then the reconciliation group. + * + * When a module registers as a listener, it must specify the type. + */ + enum ListenerType { + DeviceClassifier, // Listener should only do device classification change. + DeviceReconciler, // Listener should do reconciliation only + } + /** * Fields used in devices for indexes and querying * @see IDeviceService#addIndex @@ -189,8 +206,9 @@ public interface IDeviceService extends IFloodlightService { * Adds a listener to listen for IDeviceManagerServices notifications * * @param listener The listener that wants the notifications + * @param type The type of the listener */ - public void addListener(IDeviceListener listener); + public void addListener(IDeviceListener listener, ListenerType type); /** * Specify points in the network where attachment points are not to @@ -198,8 +216,8 @@ public interface IDeviceService extends IFloodlightService { * @param sw * @param port */ - public void addSuppressAPs(long swId, short port); + public void addSuppressAPs(long swId, short port); - public void removeSuppressAPs(long swId, short port); + public void removeSuppressAPs(long swId, short port); } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java index 9d59caac4..d2b712de8 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/Device.java @@ -62,6 +62,7 @@ public class Device implements IDevice { protected String macAddressString; // the vlan Ids from the entities of this device protected Short[] vlanIds; + protected String dhcpClientName; @@ -97,6 +98,7 @@ public class Device implements IDevice { this.entityClass = entityClass; Arrays.sort(this.entities); + this.dhcpClientName = null; this.oldAPs = null; this.attachmentPoints = null; @@ -126,12 +128,14 @@ entity.getLastSeenTimestamp().getTime()); */ public Device(DeviceManagerImpl deviceManager, Long deviceKey, + String dhcpClientName, Collection<AttachmentPoint> oldAPs, Collection<AttachmentPoint> attachmentPoints, Collection<Entity> entities, IEntityClass entityClass) { this.deviceManager = deviceManager; this.deviceKey = deviceKey; + this.dhcpClientName = dhcpClientName; this.entities = entities.toArray(new Entity[entities.size()]); this.oldAPs = null; this.attachmentPoints = null; @@ -167,6 +171,7 @@ entity.getLastSeenTimestamp().getTime()); int insertionpoint) { this.deviceManager = device.deviceManager; this.deviceKey = device.deviceKey; + this.dhcpClientName = device.dhcpClientName; this.entities = new Entity[device.entities.length + 1]; if (insertionpoint < 0) { @@ -708,6 +713,10 @@ entity.getLastSeenTimestamp().getTime()); return entities; } + public String getDHCPClientName() { + return dhcpClientName; + } + // *************** // Utility Methods // *************** diff --git a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java index 8f9281243..dff8cba7c 100755 --- a/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImpl.java @@ -61,8 +61,12 @@ import net.floodlightcontroller.flowcache.IFlowReconcileService; import net.floodlightcontroller.flowcache.OFMatchReconcile; import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LDUpdate; import net.floodlightcontroller.packet.ARP; +import net.floodlightcontroller.packet.DHCP; +import net.floodlightcontroller.packet.DHCPOption; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.UDP; +import net.floodlightcontroller.packet.DHCP.DHCPOptionCode; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.threadpool.IThreadPoolService; @@ -193,8 +197,11 @@ IFlowReconcileListener, IInfoProvider, IHAListener { /** * Device manager event listeners + * reclassifyDeviceListeners are notified first before reconcileDeviceListeners. + * This is to make sure devices are correctly reclassified before reconciliation. */ - protected Set<IDeviceListener> deviceListeners; + protected Set<IDeviceListener> reclassifyDeviceListeners; + protected Set<IDeviceListener> reconcileDeviceListeners; /** * A device update event to be dispatched @@ -519,8 +526,15 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } @Override - public void addListener(IDeviceListener listener) { - deviceListeners.add(listener); + public void addListener(IDeviceListener listener, ListenerType type) { + switch (type) { + case DeviceClassifier: + reclassifyDeviceListeners.add(listener); + break; + case DeviceReconciler: + reconcileDeviceListeners.add(listener); + break; + } } // ************* @@ -670,7 +684,8 @@ IFlowReconcileListener, IInfoProvider, IHAListener { new HashSet<EnumSet<DeviceField>>(); addIndex(true, EnumSet.of(DeviceField.IPV4)); - this.deviceListeners = new HashSet<IDeviceListener>(); + this.reclassifyDeviceListeners = new HashSet<IDeviceListener>(); + this.reconcileDeviceListeners = new HashSet<IDeviceListener>(); this.suppressAPs = Collections.newSetFromMap( new ConcurrentHashMap<SwitchPort, Boolean>()); @@ -788,9 +803,37 @@ IFlowReconcileListener, IInfoProvider, IHAListener { new Object[] { pi, sw.getStringId(), pi.getInPort(), eth, srcDevice, dstDevice }); } + + snoopDHCPClientName(eth, srcDevice); + return Command.CONTINUE; } + /** + * Snoop and record client-provided host name from DHCP requests + * @param eth + * @param srcDevice + */ + private void snoopDHCPClientName(Ethernet eth, Device srcDevice) { + if (eth.getEtherType() != Ethernet.TYPE_IPv4) + return; + IPv4 ipv4 = (IPv4) eth.getPayload(); + if (ipv4.getProtocol() != IPv4.PROTOCOL_UDP) + return; + UDP udp = (UDP) ipv4.getPayload(); + if (!(udp.getPayload() instanceof DHCP)) + return; + DHCP dhcp = (DHCP) udp.getPayload(); + byte opcode = dhcp.getOpCode(); + if (opcode == DHCP.OPCODE_REQUEST) { + DHCPOption dhcpOption = dhcp.getOption( + DHCPOptionCode.OptionCode_Hostname); + if (dhcpOption != null) { + srcDevice.dhcpClientName = new String(dhcpOption.getData()); + } + } + } + /** * Check whether the given attachment point is valid given the current * topology @@ -1280,39 +1323,44 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (logger.isTraceEnabled()) { logger.trace("Dispatching device update: {}", update); } - for (IDeviceListener listener : deviceListeners) { - switch (update.change) { - case ADD: - listener.deviceAdded(update.device); - break; - case DELETE: - listener.deviceRemoved(update.device); - break; - case CHANGE: - for (DeviceField field : update.fieldsChanged) { - switch (field) { - case IPV4: - listener.deviceIPV4AddrChanged(update.device); - break; - case SWITCH: - case PORT: - //listener.deviceMoved(update.device); - break; - case VLAN: - listener.deviceVlanChanged(update.device); - break; - default: - logger.debug("Unknown device field changed {}", - update.fieldsChanged.toString()); - break; - } + notifyListeners(reclassifyDeviceListeners, update); + notifyListeners(reconcileDeviceListeners, update); + } + } + + protected void notifyListeners(Set<IDeviceListener> listeners, DeviceUpdate update) { + for (IDeviceListener listener : listeners) { + switch (update.change) { + case ADD: + listener.deviceAdded(update.device); + break; + case DELETE: + listener.deviceRemoved(update.device); + break; + case CHANGE: + for (DeviceField field : update.fieldsChanged) { + switch (field) { + case IPV4: + listener.deviceIPV4AddrChanged(update.device); + break; + case SWITCH: + case PORT: + //listener.deviceMoved(update.device); + break; + case VLAN: + listener.deviceVlanChanged(update.device); + break; + default: + logger.debug("Unknown device field changed {}", + update.fieldsChanged.toString()); + break; } - break; - } + } + break; } } } - + /** * Check if the entity e has all the keyFields set. Returns false if not * @param e entity to check @@ -1477,6 +1525,7 @@ IFlowReconcileListener, IInfoProvider, IHAListener { if (toKeep.size() > 0) { Device newDevice = allocateDevice(d.getDeviceKey(), + d.getDHCPClientName(), d.oldAPs, d.attachmentPoints, toKeep, @@ -1595,11 +1644,13 @@ IFlowReconcileListener, IInfoProvider, IHAListener { // TODO: FIX THIS. protected Device allocateDevice(Long deviceKey, + String dhcpClientName, List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs, Collection<Entity> entities, IEntityClass entityClass) { - return new Device(this, deviceKey, aps, trueAPs, entities, entityClass); + return new Device(this, deviceKey, dhcpClientName, aps, trueAPs, + entities, entityClass); } protected Device allocateDevice(Device device, @@ -1630,8 +1681,9 @@ IFlowReconcileListener, IInfoProvider, IHAListener { } if (newAPs.isEmpty()) newAPs = null; - Device d = new Device(this, device.getDeviceKey(),newAPs, null, - entities, device.getEntityClass()); + Device d = new Device(this, device.getDeviceKey(), + device.getDHCPClientName(), newAPs, null, + entities, device.getEntityClass()); d.updateAttachmentPoint(); return d; } @@ -1677,7 +1729,10 @@ IFlowReconcileListener, IInfoProvider, IHAListener { * @param updates the updates to process. */ protected void sendDeviceMovedNotification(Device d) { - for (IDeviceListener listener : deviceListeners) { + for (IDeviceListener listener : reclassifyDeviceListeners) { + listener.deviceMoved(d); + } + for (IDeviceListener listener : reconcileDeviceListeners) { listener.deviceMoved(d); } } diff --git a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java index 66bdaef93..4e4e07db9 100644 --- a/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java +++ b/src/main/java/net/floodlightcontroller/devicemanager/web/DeviceSerializer.java @@ -64,6 +64,8 @@ public class DeviceSerializer extends JsonSerializer<Device> { jGen.writeNumberField("lastSeen", device.getLastSeen().getTime()); + jGen.writeStringField("dhcpClientName", device.getDHCPClientName()); + jGen.writeEndObject(); } diff --git a/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java b/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java index fe84079c5..8cad49b31 100644 --- a/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java +++ b/src/main/java/net/floodlightcontroller/flowcache/FCQueryObj.java @@ -68,11 +68,7 @@ public class FCQueryObj { if (vlan != null) { this.vlans = new Short[] { vlan }; } else { - if (srcDevice != null) { - this.vlans = srcDevice.getVlanId(); - } else if (dstDevice != null) { - this.vlans = dstDevice.getVlanId(); - } + this.vlans = null; } } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java index 84d1cc958..441347e20 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java @@ -19,6 +19,9 @@ package net.floodlightcontroller.linkdiscovery; import java.util.Map; import java.util.Set; + +import org.openflow.protocol.OFPacketOut; + import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.routing.Link; import net.floodlightcontroller.topology.NodePortTuple; @@ -44,6 +47,15 @@ public interface ILinkDiscoveryService extends IFloodlightService { */ public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info); + /** + * Returns OFPacketOut which contains the LLDP data corresponding + * to switchport (sw, port). PacketOut does not contain actions. + * PacketOut length includes the minimum length and data length. + */ + public OFPacketOut generateLLDPMessage(long sw, short port, + boolean isStandard, + boolean isReverse); + /** * Returns an unmodifiable map from switch id to a set of all links with it * as an endpoint. @@ -91,5 +103,5 @@ public interface ILinkDiscoveryService extends IFloodlightService { * Get the map of node-port tuples from link DB */ public Map<NodePortTuple, Set<Link>> getPortLinks(); - + } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 3191ab7a9..107114b4d 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -518,6 +518,20 @@ public class LinkDiscoveryManager implements IOFMessageListener, sendDiscoveryMessage(sw, port, true, false); } + /** + * 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; + } + /** * Check if incoming discovery messages are enabled or not. * @param sw @@ -606,31 +620,8 @@ public class LinkDiscoveryManager implements IOFMessageListener, return actions; } - /** - * 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; + public OFPacketOut generateLLDPMessage(long sw, short port, + boolean isStandard, boolean isReverse) { IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); OFPhysicalPort ofpPort = iofSwitch.getPort(port); @@ -638,6 +629,7 @@ public class LinkDiscoveryManager implements IOFMessageListener, if (log.isTraceEnabled()) { log.trace("Sending LLDP packet out of swich: {}, port: {}", HexString.toHexString(sw), port); + return null; } // using "nearest customer bridge" MAC address for broadest possible @@ -731,9 +723,52 @@ public class LinkDiscoveryManager implements IOFMessageListener, 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; + } + + /** + * 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); + 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()) { @@ -741,10 +776,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, } po.setActionsLength(actionLength); - // set data - po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() - + data.length); - po.setPacketData(data); + // po already has the minimum length + data length set + // simply add the actions length to this. + po.setLengthU(po.getLengthU() + po.getActionsLength()); // send try { @@ -754,7 +788,6 @@ public class LinkDiscoveryManager implements IOFMessageListener, log.error("Failure sending LLDP out port {} on switch {}", new Object[] { port, iofSwitch.getStringId() }, e); } - } /** @@ -854,12 +887,12 @@ public class LinkDiscoveryManager implements IOFMessageListener, return Command.CONTINUE; } - private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, + 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, pi.getInPort(), isStandard)) + if (!isIncomingDiscoveryAllowed(sw, inPort, isStandard)) return Command.STOP; // If this is a malformed LLDP, or not from us, exit @@ -946,10 +979,10 @@ public class LinkDiscoveryManager implements IOFMessageListener, } return Command.STOP; } - if (!iofSwitch.portEnabled(pi.getInPort())) { + if (!iofSwitch.portEnabled(inPort)) { if (log.isTraceEnabled()) { log.trace("Ignoring link with disabled dest port: switch {} port {}", - HexString.toHexString(sw), pi.getInPort()); + HexString.toHexString(sw), inPort); } return Command.STOP; } @@ -957,15 +990,20 @@ public class LinkDiscoveryManager implements IOFMessageListener, OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort); int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0; - physicalPort = iofSwitch.getPort(pi.getInPort()); + physicalPort = iofSwitch.getPort(inPort); int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0; // Store the time of update to this link, and push it out to // routingEngine Link lt = new Link(remoteSwitch.getId(), remotePort, - iofSwitch.getId(), pi.getInPort()); + iofSwitch.getId(), inPort); + if (!isLinkAllowed(lt.getSrc(), lt.getSrcPort(), + lt.getDst(), lt.getDstPort())) + return Command.STOP; + + // Continue only if link is allowed. Long lastLldpTime = null; Long lastBddpTime = null; @@ -980,8 +1018,11 @@ public class LinkDiscoveryManager implements IOFMessageListener, lastBddpTime, srcPortState, dstPortState); + addOrUpdateLink(lt, newLinkInfo); + // Continue only if addOrUpdateLink was successful. + // 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 @@ -1043,9 +1084,9 @@ public class LinkDiscoveryManager implements IOFMessageListener, // continue with the regular processing. if (bsn.getPayload() instanceof LLDP == false) return Command.CONTINUE; - return handleLldp((LLDP) bsn.getPayload(), sw, pi, false, cntx); + return handleLldp((LLDP) bsn.getPayload(), sw, pi.getInPort(), false, cntx); } else if (eth.getEtherType() == Ethernet.TYPE_LLDP) { - return handleLldp((LLDP) eth.getPayload(), sw, pi, true, cntx); + 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) { diff --git a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java index 90d6829c9..e67a2fb29 100644 --- a/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java +++ b/src/main/java/net/floodlightcontroller/loadbalancer/LoadBalancer.java @@ -24,8 +24,11 @@ import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.openflow.protocol.OFFlowMod; import org.openflow.protocol.OFMatch; @@ -35,7 +38,18 @@ import org.openflow.protocol.OFPacketOut; import org.openflow.protocol.OFPort; import org.openflow.protocol.OFType; import org.openflow.protocol.action.OFAction; +import org.openflow.protocol.action.OFActionDataLayerDestination; +import org.openflow.protocol.action.OFActionDataLayerSource; +import org.openflow.protocol.action.OFActionEnqueue; +import org.openflow.protocol.action.OFActionNetworkLayerDestination; +import org.openflow.protocol.action.OFActionNetworkLayerSource; +import org.openflow.protocol.action.OFActionNetworkTypeOfService; import org.openflow.protocol.action.OFActionOutput; +import org.openflow.protocol.action.OFActionStripVirtualLan; +import org.openflow.protocol.action.OFActionTransportLayerDestination; +import org.openflow.protocol.action.OFActionTransportLayerSource; +import org.openflow.protocol.action.OFActionVirtualLanIdentifier; +import org.openflow.protocol.action.OFActionVirtualLanPriorityCodePoint; import org.openflow.util.HexString; import org.openflow.util.U16; import org.slf4j.Logger; @@ -63,7 +77,6 @@ import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.routing.IRoutingService; import net.floodlightcontroller.routing.Route; import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService; -import net.floodlightcontroller.staticflowentry.StaticFlowEntries; import net.floodlightcontroller.topology.ITopologyService; import net.floodlightcontroller.topology.NodePortTuple; import net.floodlightcontroller.util.MACAddress; @@ -556,7 +569,7 @@ public class LoadBalancer implements IFloodlightModule, } - StaticFlowEntries.parseActionString(fm, actionString, log); + parseActionString(fm, actionString, log); fm.setPriority(U16.t(LB_PRIORITY)); @@ -824,4 +837,491 @@ public class LoadBalancer implements IFloodlightModule, restApi.addRestletRoutable(new LoadBalancerWebRoutable()); } + // Utilities borrowed from StaticFlowEntries + + private static class SubActionStruct { + OFAction action; + int len; + } + + /** + * Parses OFFlowMod actions from strings. + * @param flowMod The OFFlowMod to set the actions for + * @param actionstr The string containing all the actions + * @param log A logger to log for errors. + */ + public static void parseActionString(OFFlowMod flowMod, String actionstr, Logger log) { + List<OFAction> actions = new LinkedList<OFAction>(); + int actionsLength = 0; + if (actionstr != null) { + actionstr = actionstr.toLowerCase(); + for (String subaction : actionstr.split(",")) { + String action = subaction.split("[=:]")[0]; + SubActionStruct subaction_struct = null; + + if (action.equals("output")) { + subaction_struct = decode_output(subaction, log); + } + else if (action.equals("enqueue")) { + subaction_struct = decode_enqueue(subaction, log); + } + else if (action.equals("strip-vlan")) { + subaction_struct = decode_strip_vlan(subaction, log); + } + else if (action.equals("set-vlan-id")) { + subaction_struct = decode_set_vlan_id(subaction, log); + } + else if (action.equals("set-vlan-priority")) { + subaction_struct = decode_set_vlan_priority(subaction, log); + } + else if (action.equals("set-src-mac")) { + subaction_struct = decode_set_src_mac(subaction, log); + } + else if (action.equals("set-dst-mac")) { + subaction_struct = decode_set_dst_mac(subaction, log); + } + else if (action.equals("set-tos-bits")) { + subaction_struct = decode_set_tos_bits(subaction, log); + } + else if (action.equals("set-src-ip")) { + subaction_struct = decode_set_src_ip(subaction, log); + } + else if (action.equals("set-dst-ip")) { + subaction_struct = decode_set_dst_ip(subaction, log); + } + else if (action.equals("set-src-port")) { + subaction_struct = decode_set_src_port(subaction, log); + } + else if (action.equals("set-dst-port")) { + subaction_struct = decode_set_dst_port(subaction, log); + } + else { + log.error("Unexpected action '{}', '{}'", action, subaction); + } + + if (subaction_struct != null) { + actions.add(subaction_struct.action); + actionsLength += subaction_struct.len; + } + } + } + log.debug("action {}", actions); + + flowMod.setActions(actions); + flowMod.setLengthU(OFFlowMod.MINIMUM_LENGTH + actionsLength); + } + + private static SubActionStruct decode_output(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n; + + n = Pattern.compile("output=(?:((?:0x)?\\d+)|(all)|(controller)|(local)|(ingress-port)|(normal)|(flood))").matcher(subaction); + if (n.matches()) { + OFActionOutput action = new OFActionOutput(); + action.setMaxLength((short) Short.MAX_VALUE); + short port = OFPort.OFPP_NONE.getValue(); + if (n.group(1) != null) { + try { + port = get_short(n.group(1)); + } + catch (NumberFormatException e) { + log.debug("Invalid port in: '{}' (error ignored)", subaction); + return null; + } + } + else if (n.group(2) != null) + port = OFPort.OFPP_ALL.getValue(); + else if (n.group(3) != null) + port = OFPort.OFPP_CONTROLLER.getValue(); + else if (n.group(4) != null) + port = OFPort.OFPP_LOCAL.getValue(); + else if (n.group(5) != null) + port = OFPort.OFPP_IN_PORT.getValue(); + else if (n.group(6) != null) + port = OFPort.OFPP_NORMAL.getValue(); + else if (n.group(7) != null) + port = OFPort.OFPP_FLOOD.getValue(); + action.setPort(port); + log.debug("action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionOutput.MINIMUM_LENGTH; + } + else { + log.error("Invalid subaction: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_enqueue(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n; + + n = Pattern.compile("enqueue=(?:((?:0x)?\\d+)\\:((?:0x)?\\d+))").matcher(subaction); + if (n.matches()) { + short portnum = 0; + if (n.group(1) != null) { + try { + portnum = get_short(n.group(1)); + } + catch (NumberFormatException e) { + log.debug("Invalid port-num in: '{}' (error ignored)", subaction); + return null; + } + } + + int queueid = 0; + if (n.group(2) != null) { + try { + queueid = get_int(n.group(2)); + } + catch (NumberFormatException e) { + log.debug("Invalid queue-id in: '{}' (error ignored)", subaction); + return null; + } + } + + OFActionEnqueue action = new OFActionEnqueue(); + action.setPort(portnum); + action.setQueueId(queueid); + log.debug("action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionEnqueue.MINIMUM_LENGTH; + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_strip_vlan(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("strip-vlan").matcher(subaction); + + if (n.matches()) { + OFActionStripVirtualLan action = new OFActionStripVirtualLan(); + log.debug("action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionStripVirtualLan.MINIMUM_LENGTH; + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_vlan_id(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-vlan-id=((?:0x)?\\d+)").matcher(subaction); + + if (n.matches()) { + if (n.group(1) != null) { + try { + short vlanid = get_short(n.group(1)); + OFActionVirtualLanIdentifier action = new OFActionVirtualLanIdentifier(); + action.setVirtualLanIdentifier(vlanid); + log.debug(" action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionVirtualLanIdentifier.MINIMUM_LENGTH; + } + catch (NumberFormatException e) { + log.debug("Invalid VLAN in: {} (error ignored)", subaction); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_vlan_priority(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-vlan-priority=((?:0x)?\\d+)").matcher(subaction); + + if (n.matches()) { + if (n.group(1) != null) { + try { + byte prior = get_byte(n.group(1)); + OFActionVirtualLanPriorityCodePoint action = new OFActionVirtualLanPriorityCodePoint(); + action.setVirtualLanPriorityCodePoint(prior); + log.debug(" action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionVirtualLanPriorityCodePoint.MINIMUM_LENGTH; + } + catch (NumberFormatException e) { + log.debug("Invalid VLAN priority in: {} (error ignored)", subaction); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_src_mac(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-src-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); + + if (n.matches()) { + byte[] macaddr = get_mac_addr(n, subaction, log); + if (macaddr != null) { + OFActionDataLayerSource action = new OFActionDataLayerSource(); + action.setDataLayerAddress(macaddr); + log.debug("action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionDataLayerSource.MINIMUM_LENGTH; + } + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_dst_mac(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-dst-mac=(?:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+)\\:(\\p{XDigit}+))").matcher(subaction); + + if (n.matches()) { + byte[] macaddr = get_mac_addr(n, subaction, log); + if (macaddr != null) { + OFActionDataLayerDestination action = new OFActionDataLayerDestination(); + action.setDataLayerAddress(macaddr); + log.debug(" action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionDataLayerDestination.MINIMUM_LENGTH; + } + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_tos_bits(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-tos-bits=((?:0x)?\\d+)").matcher(subaction); + + if (n.matches()) { + if (n.group(1) != null) { + try { + byte tosbits = get_byte(n.group(1)); + OFActionNetworkTypeOfService action = new OFActionNetworkTypeOfService(); + action.setNetworkTypeOfService(tosbits); + log.debug(" action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionNetworkTypeOfService.MINIMUM_LENGTH; + } + catch (NumberFormatException e) { + log.debug("Invalid dst-port in: {} (error ignored)", subaction); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_src_ip(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-src-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); + + if (n.matches()) { + int ipaddr = get_ip_addr(n, subaction, log); + OFActionNetworkLayerSource action = new OFActionNetworkLayerSource(); + action.setNetworkAddress(ipaddr); + log.debug(" action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionNetworkLayerSource.MINIMUM_LENGTH; + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_dst_ip(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-dst-ip=(?:(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+))").matcher(subaction); + + if (n.matches()) { + int ipaddr = get_ip_addr(n, subaction, log); + OFActionNetworkLayerDestination action = new OFActionNetworkLayerDestination(); + action.setNetworkAddress(ipaddr); + log.debug("action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionNetworkLayerDestination.MINIMUM_LENGTH; + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_src_port(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-src-port=((?:0x)?\\d+)").matcher(subaction); + + if (n.matches()) { + if (n.group(1) != null) { + try { + short portnum = get_short(n.group(1)); + OFActionTransportLayerSource action = new OFActionTransportLayerSource(); + action.setTransportPort(portnum); + log.debug("action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionTransportLayerSource.MINIMUM_LENGTH;; + } + catch (NumberFormatException e) { + log.debug("Invalid src-port in: {} (error ignored)", subaction); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static SubActionStruct decode_set_dst_port(String subaction, Logger log) { + SubActionStruct sa = null; + Matcher n = Pattern.compile("set-dst-port=((?:0x)?\\d+)").matcher(subaction); + + if (n.matches()) { + if (n.group(1) != null) { + try { + short portnum = get_short(n.group(1)); + OFActionTransportLayerDestination action = new OFActionTransportLayerDestination(); + action.setTransportPort(portnum); + log.debug("action {}", action); + + sa = new SubActionStruct(); + sa.action = action; + sa.len = OFActionTransportLayerDestination.MINIMUM_LENGTH;; + } + catch (NumberFormatException e) { + log.debug("Invalid dst-port in: {} (error ignored)", subaction); + return null; + } + } + } + else { + log.debug("Invalid action: '{}'", subaction); + return null; + } + + return sa; + } + + private static byte[] get_mac_addr(Matcher n, String subaction, Logger log) { + byte[] macaddr = new byte[6]; + + for (int i=0; i<6; i++) { + if (n.group(i+1) != null) { + try { + macaddr[i] = get_byte("0x" + n.group(i+1)); + } + catch (NumberFormatException e) { + log.debug("Invalid src-mac in: '{}' (error ignored)", subaction); + return null; + } + } + else { + log.debug("Invalid src-mac in: '{}' (null, error ignored)", subaction); + return null; + } + } + + return macaddr; + } + + private static int get_ip_addr(Matcher n, String subaction, Logger log) { + int ipaddr = 0; + + for (int i=0; i<4; i++) { + if (n.group(i+1) != null) { + try { + ipaddr = ipaddr<<8; + ipaddr = ipaddr | get_int(n.group(i+1)); + } + catch (NumberFormatException e) { + log.debug("Invalid src-ip in: '{}' (error ignored)", subaction); + return 0; + } + } + else { + log.debug("Invalid src-ip in: '{}' (null, error ignored)", subaction); + return 0; + } + } + + return ipaddr; + } + + // Parse int as decimal, hex (start with 0x or #) or octal (starts with 0) + private static int get_int(String str) { + return (int)Integer.decode(str); + } + + // Parse short as decimal, hex (start with 0x or #) or octal (starts with 0) + private static short get_short(String str) { + return (short)(int)Integer.decode(str); + } + + // Parse byte as decimal, hex (start with 0x or #) or octal (starts with 0) + private static byte get_byte(String str) { + return Integer.decode(str).byteValue(); + } + + } diff --git a/src/main/java/net/floodlightcontroller/packet/DHCP.java b/src/main/java/net/floodlightcontroller/packet/DHCP.java index 5ad215e57..fbc2d4353 100644 --- a/src/main/java/net/floodlightcontroller/packet/DHCP.java +++ b/src/main/java/net/floodlightcontroller/packet/DHCP.java @@ -63,6 +63,7 @@ public class DHCP extends BasePacket { public enum DHCPOptionCode { OptionCode_SubnetMask ((byte)1), + OptionCode_Hostname ((byte)12), OptionCode_RequestedIP ((byte)50), OptionCode_LeaseTime ((byte)51), OptionCode_MessageType ((byte)53), diff --git a/src/main/java/net/floodlightcontroller/packet/LLDP.java b/src/main/java/net/floodlightcontroller/packet/LLDP.java index 5a1104104..130f1c731 100644 --- a/src/main/java/net/floodlightcontroller/packet/LLDP.java +++ b/src/main/java/net/floodlightcontroller/packet/LLDP.java @@ -105,7 +105,8 @@ public class LLDP extends BasePacket { int length = 2+this.chassisId.getLength() + 2+this.portId.getLength() + 2+this.ttl.getLength() + 2; for (LLDPTLV tlv : this.optionalTLVList) { - length += 2 + tlv.getLength(); + if (tlv != null) + length += 2 + tlv.getLength(); } byte[] data = new byte[length]; @@ -114,7 +115,7 @@ public class LLDP extends BasePacket { bb.put(this.portId.serialize()); bb.put(this.ttl.serialize()); for (LLDPTLV tlv : this.optionalTLVList) { - bb.put(tlv.serialize()); + if (tlv != null) bb.put(tlv.serialize()); } bb.putShort((short) 0); // End of LLDPDU diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java index cd0cd3692..5a6827f49 100644 --- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java +++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java @@ -34,8 +34,6 @@ import net.floodlightcontroller.core.annotations.LogMessageDoc; import net.floodlightcontroller.core.annotations.LogMessageDocs; import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.counter.ICounterStoreService; -import net.floodlightcontroller.devicemanager.IDevice; -import net.floodlightcontroller.devicemanager.IDeviceListener; import net.floodlightcontroller.devicemanager.IDeviceService; import net.floodlightcontroller.devicemanager.SwitchPort; import net.floodlightcontroller.packet.Ethernet; @@ -66,7 +64,7 @@ import org.slf4j.LoggerFactory; */ @LogMessageCategory("Flow Programming") public abstract class ForwardingBase - implements IOFMessageListener, IDeviceListener { + implements IOFMessageListener { protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class); @@ -124,7 +122,6 @@ public abstract class ForwardingBase * Adds a listener for devicemanager and registers for PacketIns. */ protected void startUp() { - deviceManager.addListener(this); floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); } @@ -592,30 +589,6 @@ public abstract class ForwardingBase } - @Override - public void deviceAdded(IDevice device) { - // NOOP - } - - @Override - public void deviceRemoved(IDevice device) { - // NOOP - } - - @Override - public void deviceMoved(IDevice device) { - } - - @Override - public void deviceIPV4AddrChanged(IDevice device) { - - } - - @Override - public void deviceVlanChanged(IDevice device) { - - } - @Override public boolean isCallbackOrderingPrereq(OFType type, String name) { return (type.equals(OFType.PACKET_IN) && diff --git a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java index 347d21893..ed85b57ac 100644 --- a/src/main/java/net/floodlightcontroller/topology/TopologyManager.java +++ b/src/main/java/net/floodlightcontroller/topology/TopologyManager.java @@ -117,11 +117,11 @@ public class TopologyManager implements protected BlockingQueue<LDUpdate> ldUpdates; protected List<LDUpdate> appliedUpdates; - + // These must be accessed using getCurrentInstance(), not directly protected TopologyInstance currentInstance; protected TopologyInstance currentInstanceWithoutTunnels; - + protected SingletonTask newInstanceTask; private Date lastUpdateTime; @@ -140,10 +140,29 @@ public class TopologyManager implements */ protected boolean tunnelPortsUpdated; - protected final int TOPOLOGY_COMPUTE_INTERVAL_MS = 500; + protected int TOPOLOGY_COMPUTE_INTERVAL_MS = 500; + + // Getter/Setter methods + /** + * Get the time interval for the period topology updates, if any. + * The time returned is in milliseconds. + * @return + */ + public int getTopologyComputeInterval() { + return TOPOLOGY_COMPUTE_INTERVAL_MS; + } + + /** + * Set the time interval for the period topology updates, if any. + * The time is in milliseconds. + * @return + */ + public void setTopologyComputeInterval(int time_ms) { + TOPOLOGY_COMPUTE_INTERVAL_MS = time_ms; + } /** - * Thread for recomputing topology. The thread is always running, + * Thread for recomputing topology. The thread is always running, * however the function applyUpdates() has a blocking call. */ @LogMessageDoc(level="ERROR", @@ -157,6 +176,7 @@ public class TopologyManager implements try { if (ldUpdates.peek() != null) updateTopology(); + handleMiscellaneousPeriodicEvents(); } catch (Exception e) { log.error("Error in topology instance task thread", e); @@ -168,6 +188,11 @@ public class TopologyManager implements } } + // To be used for adding any periodic events that's required by topology. + protected void handleMiscellaneousPeriodicEvents() { + return; + } + public boolean updateTopology() { boolean newInstanceFlag; linksUpdated = false; @@ -1206,23 +1231,35 @@ public class TopologyManager implements return result1 || result2; } + protected void addOrUpdateTunnelLink(long srcId, short srcPort, long dstId, + short dstPort) { + // If you need to handle tunnel links, this is a placeholder. + } + public void addOrUpdateLink(long srcId, short srcPort, long dstId, short dstPort, LinkType type) { Link link = new Link(srcId, srcPort, dstId, dstPort); - addPortToSwitch(srcId, srcPort); - addPortToSwitch(dstId, dstPort); - - addLinkToStructure(switchPortLinks, link); if (type.equals(LinkType.MULTIHOP_LINK)) { + addPortToSwitch(srcId, srcPort); + addPortToSwitch(dstId, dstPort); + addLinkToStructure(switchPortLinks, link); + addLinkToStructure(portBroadcastDomainLinks, link); dtLinksUpdated = removeLinkFromStructure(directLinks, link); + linksUpdated = true; } else if (type.equals(LinkType.DIRECT_LINK)) { + addPortToSwitch(srcId, srcPort); + addPortToSwitch(dstId, dstPort); + addLinkToStructure(switchPortLinks, link); + addLinkToStructure(directLinks, link); removeLinkFromStructure(portBroadcastDomainLinks, link); dtLinksUpdated = true; + linksUpdated = true; + } else if (type.equals(LinkType.TUNNEL)) { + addOrUpdateTunnelLink(srcId, srcPort, dstId, dstPort); } - linksUpdated = true; } public void removeLink(Link link) { diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java index 684d0687a..87dfc4a39 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/VirtualNetworkFilter.java @@ -312,7 +312,7 @@ public class VirtualNetworkFilter public void startUp(FloodlightModuleContext context) { floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); restApi.addRestletRoutable(new VirtualNetworkWebRoutable()); - deviceService.addListener(this); + deviceService.addListener(this, IDeviceService.ListenerType.DeviceClassifier); } // IOFMessageListener diff --git a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java index f9169983a..f6f94f880 100644 --- a/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java +++ b/src/test/java/net/floodlightcontroller/core/internal/ControllerTest.java @@ -762,6 +762,7 @@ public class ControllerTest extends FloodlightTestCase replay(roleChanger); controller.setRole(Role.SLAVE); + controller.doSetRole(); // avoid wait verify(roleChanger); Controller.IUpdate upd = controller.updates.poll(); @@ -1531,6 +1532,7 @@ public class ControllerTest extends FloodlightTestCase expectLastCall().andReturn(Command.STOP).once(); replay(sw, listener); controller.setRole(Role.SLAVE); + controller.doSetRole(); // avoid the wait chdlr.processOFMessage(pi); verify(sw, listener); assertEquals(1, controller.updates.size()); @@ -1550,6 +1552,7 @@ public class ControllerTest extends FloodlightTestCase resetToDefault(listener); replay(listener); controller.setRole(Role.MASTER); + controller.doSetRole(); // avoid the wait chdlr.processOFMessage(pi); verify(listener); assertEquals(1, controller.updates.size()); diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java index 2e64e99b3..5e3d764df 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceManagerImplTest.java @@ -277,7 +277,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDeviceListener mockListener = createStrictMock(IDeviceListener.class); - deviceManager.addListener(mockListener); + deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier); deviceManager.entityClassifier= new MockEntityClassifier(); deviceManager.startUp(null); @@ -483,7 +483,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDeviceListener mockListener = createStrictMock(IDeviceListener.class); - deviceManager.addListener(mockListener); + deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier); ITopologyService mockTopology = createMock(ITopologyService.class); expect(mockTopology.getL2DomainId(1L)). @@ -591,7 +591,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDeviceListener mockListener = createMock(IDeviceListener.class); - deviceManager.addListener(mockListener); + deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier); ITopologyService mockTopology = createMock(ITopologyService.class); expect(mockTopology.getL2DomainId(1L)). @@ -706,7 +706,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { IDeviceListener mockListener = createMock(IDeviceListener.class); - deviceManager.addListener(mockListener); + deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier); ITopologyService mockTopology = createMock(ITopologyService.class); expect(mockTopology.getL2DomainId(1L)). @@ -1097,7 +1097,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { assertEquals(d.getDeviceKey(), diter.next().getDeviceKey()); - deviceManager.addListener(mockListener); + deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier); replay(mockListener); deviceManager.entityCleanupTask.reschedule(0, null); @@ -1160,7 +1160,7 @@ public class DeviceManagerImplTest extends FloodlightTestCase { d = deviceManager.learnDeviceByEntity(entity1); assertArrayEquals(new Integer[] { 1, 2 }, d.getIPv4Addresses()); - deviceManager.addListener(mockListener); + deviceManager.addListener(mockListener, IDeviceService.ListenerType.DeviceClassifier); replay(mockListener); deviceManager.entityCleanupTask.reschedule(0, null); @@ -1877,7 +1877,8 @@ public class DeviceManagerImplTest extends FloodlightTestCase { Entity[] entities = new Entity[] { entity1, entity2, entity3, entity4 }; - Device d = new Device(null,1L, null, null, Arrays.asList(entities), null); + Device d = new Device(null,1L, null, null, null, + Arrays.asList(entities), null); SwitchPort swp1x1 = new SwitchPort(1L, 1); SwitchPort swp1x2 = new SwitchPort(1L, 2); SwitchPort swp2x1 = new SwitchPort(2L, 1); diff --git a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java index 2a6ee9d09..1ac1f1c35 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/internal/DeviceUniqueIndexTest.java @@ -51,7 +51,8 @@ public class DeviceUniqueIndexTest extends TestCase { List<Entity> d1Entities = new ArrayList<Entity>(2); d1Entities.add(e1a); d1Entities.add(e1b); - d1 = new Device(null, Long.valueOf(1), null, null, d1Entities, null); + d1 = new Device(null, Long.valueOf(1), null, null, null, + d1Entities, null); // e2 and e2 alt match in MAC and VLAN e2 = new Entity(2L, (short)2, 2, 2L, 2, new Date()); @@ -145,7 +146,8 @@ public class DeviceUniqueIndexTest extends TestCase { // all key fields are null idx2.updateIndex(e4, 4L); assertEquals(null, idx2.findByEntity(e4)); - Device d4 = new Device(null, 4L, null, null, Collections.<Entity>singleton(e4), null); + Device d4 = new Device(null, 4L, null, null, null, + Collections.<Entity>singleton(e4), null); idx2.updateIndex(d4, 4L); assertEquals(null, idx2.findByEntity(e4)); diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java index 1c2f7d1b4..d05197019 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDevice.java @@ -51,7 +51,8 @@ public class MockDevice extends Device { List<AttachmentPoint> trueAPs, Collection<Entity> entities, IEntityClass entityClass) { - super(deviceManager, deviceKey, aps, trueAPs, entities, entityClass); + super(deviceManager, deviceKey, null, aps, trueAPs, + entities, entityClass); } @Override diff --git a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java index 73023c98d..dd105bba8 100644 --- a/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java +++ b/src/test/java/net/floodlightcontroller/devicemanager/test/MockDeviceManager.java @@ -63,9 +63,11 @@ public class MockDeviceManager extends DeviceManagerImpl { Integer ipv4Address, Long switchDPID, Integer switchPort, boolean processUpdates) { - Set<IDeviceListener> listeners = deviceListeners; + Set<IDeviceListener> reclassifyListeners = reclassifyDeviceListeners; + Set<IDeviceListener> reconcileListeners = reconcileDeviceListeners; if (!processUpdates) { - deviceListeners = Collections.<IDeviceListener>emptySet(); + reclassifyDeviceListeners = Collections.<IDeviceListener>emptySet(); + reconcileDeviceListeners = Collections.<IDeviceListener>emptySet(); } if (vlan != null && vlan.shortValue() <= 0) @@ -75,7 +77,8 @@ public class MockDeviceManager extends DeviceManagerImpl { IDevice res = learnDeviceByEntity(new Entity(macAddress, vlan, ipv4Address, switchDPID, switchPort, new Date())); - deviceListeners = listeners; + reclassifyDeviceListeners = reclassifyListeners; + reconcileDeviceListeners = reconcileListeners; return res; } @@ -109,6 +112,7 @@ public class MockDeviceManager extends DeviceManagerImpl { @Override protected Device allocateDevice(Long deviceKey, + String dhcpClientName, List<AttachmentPoint> aps, List<AttachmentPoint> trueAPs, Collection<Entity> entities, diff --git a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java index fe9f572e9..487e36ee7 100644 --- a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java +++ b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java @@ -95,7 +95,7 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { return ldm; } - public IOFSwitch createMockSwitch(Long id) { + private IOFSwitch createMockSwitch(Long id) { IOFSwitch mockSwitch = createNiceMock(IOFSwitch.class); expect(mockSwitch.getId()).andReturn(id).anyTimes(); return mockSwitch; -- GitLab