alshabib

link discovery and providers are implemented

......@@ -2,7 +2,7 @@ package org.onlab.onos.net;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
import com.google.common.base.MoreObjects;
/**
* Abstraction of a network connection point expressed as a pair of the
......@@ -42,13 +42,12 @@ public class ConnectPoint {
* @throws java.lang.IllegalStateException if connection point is not
* associated with a device
*/
@SuppressWarnings("unchecked")
public DeviceId deviceId() {
if (elementId instanceof DeviceId) {
return (DeviceId) elementId;
}
throw new IllegalStateException("Connection point not associated " +
"with an infrastructure device");
"with an infrastructure device");
}
/**
......@@ -77,7 +76,7 @@ public class ConnectPoint {
@Override
public String toString() {
return toStringHelper(this)
return MoreObjects.toStringHelper(this)
.add("elementId", elementId)
.add("portNumber", portNumber)
.toString();
......
package org.onlab.onos.net.trivial.impl;
import com.google.common.collect.Sets;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Set;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -24,10 +28,7 @@ import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
import org.slf4j.Logger;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.slf4j.LoggerFactory.getLogger;
import com.google.common.collect.Sets;
/**
* Provides basic implementation of the link SB & NB APIs.
......@@ -35,8 +36,8 @@ import static org.slf4j.LoggerFactory.getLogger;
@Component(immediate = true)
@Service
public class SimpleLinkManager
extends AbstractProviderRegistry<LinkProvider, LinkProviderService>
implements LinkService, LinkAdminService, LinkProviderRegistry {
extends AbstractProviderRegistry<LinkProvider, LinkProviderService>
implements LinkService, LinkAdminService, LinkProviderRegistry {
private static final String DEVICE_ID_NULL = "Device ID cannot be null";
private static final String LINK_DESC_NULL = "Link description cannot be null";
......@@ -45,7 +46,7 @@ public class SimpleLinkManager
private final Logger log = getLogger(getClass());
private final AbstractListenerRegistry<LinkEvent, LinkListener>
listenerRegistry = new AbstractListenerRegistry<>();
listenerRegistry = new AbstractListenerRegistry<>();
private final SimpleLinkStore store = new SimpleLinkStore();
......@@ -83,7 +84,7 @@ public class SimpleLinkManager
public Set<Link> getDeviceLinks(DeviceId deviceId) {
checkNotNull(deviceId, DEVICE_ID_NULL);
return Sets.union(store.getDeviceEgressLinks(deviceId),
store.getDeviceIngressLinks(deviceId));
store.getDeviceIngressLinks(deviceId));
}
@Override
......@@ -102,7 +103,7 @@ public class SimpleLinkManager
public Set<Link> getLinks(ConnectPoint connectPoint) {
checkNotNull(connectPoint, CONNECT_POINT_NULL);
return Sets.union(store.getEgressLinks(connectPoint),
store.getIngressLinks(connectPoint));
store.getIngressLinks(connectPoint));
}
@Override
......@@ -146,7 +147,7 @@ public class SimpleLinkManager
// Personalized link provider service issued to the supplied provider.
private class InternalLinkProviderService extends AbstractProviderService<LinkProvider>
implements LinkProviderService {
implements LinkProviderService {
public InternalLinkProviderService(LinkProvider provider) {
super(provider);
......@@ -156,9 +157,9 @@ public class SimpleLinkManager
public void linkDetected(LinkDescription linkDescription) {
checkNotNull(linkDescription, LINK_DESC_NULL);
checkValidity();
log.info("Link {} detected", linkDescription);
log.debug("Link {} detected", linkDescription);
LinkEvent event = store.createOrUpdateLink(provider().id(),
linkDescription);
linkDescription);
post(event);
}
......@@ -168,7 +169,7 @@ public class SimpleLinkManager
checkValidity();
log.info("Link {} vanished", linkDescription);
LinkEvent event = store.removeLink(linkDescription.src(),
linkDescription.dst());
linkDescription.dst());
post(event);
}
......
......@@ -75,4 +75,9 @@ public final class DefaultPacketContext implements PacketContext {
return new DefaultPacketContext(s, pkt);
}
@Override
public Integer inPort() {
return pktin.getInPort().getPortNumber();
}
}
......
......@@ -48,4 +48,10 @@ public interface PacketContext {
* @return the dpid of the switch.
*/
public Dpid dpid();
/**
* Provide the port on which the packet arrived.
* @return the port
*/
public Integer inPort();
}
......
......@@ -415,12 +415,14 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
OFDescStatsReply drep = (OFDescStatsReply) m;
// Here is where we differentiate between different kinds of switches
h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
h.sw.setOFVersion(h.ofVersion);
h.sw.setFeaturesReply(h.featuresReply);
h.sw.setPortDescReply(h.portDescReply);
h.sw.setConnected(true);
h.sw.setChannel(h.channel);
boolean success = h.sw.connectSwitch();
if (!success) {
disconnectDuplicate(h);
return;
......@@ -432,10 +434,10 @@ class OFChannelHandler extends IdleStateAwareChannelHandler {
log.info("Switch {} bound to class {}, description {}",
new Object[] {h.sw, h.sw.getClass(), drep });
//Put switch in EQUAL mode until we hear back from the global registry
log.debug("Setting new switch {} to EQUAL and sending Role request",
h.sw.getStringId());
h.sw.activateEqualSwitch();
h.setSwitchRole(RoleState.EQUAL);
//log.debug("Setting new switch {} to EQUAL and sending Role request",
// h.sw.getStringId());
//h.sw.activateEqualSwitch();
//h.setSwitchRole(RoleState.EQUAL);
h.sw.startDriverHandshake();
h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
......
package org.onlab.onos.of.controller.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.of.controller.DefaultPacketContext;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.OpenFlowController;
import org.onlab.onos.of.controller.OpenFlowSwitch;
......@@ -12,18 +21,11 @@ import org.onlab.onos.of.controller.PacketListener;
import org.onlab.onos.of.controller.RoleState;
import org.onlab.onos.of.controller.driver.OpenFlowAgent;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Component(immediate = true)
@Service
public class OpenFlowControllerImpl implements OpenFlowController {
......@@ -42,238 +44,244 @@ public class OpenFlowControllerImpl implements OpenFlowController {
protected Set<OpenFlowSwitchListener> ofEventListener =
new HashSet<>();
protected List<PacketListener> ofPacketListener =
new ArrayList<>();
private final Controller ctrl = new Controller();
@Activate
public void activate() {
ctrl.start(agent);
}
@Deactivate
public void deactivate() {
ctrl.stop();
}
@Override
public Iterable<OpenFlowSwitch> getSwitches() {
return connectedSwitches.values();
}
@Override
public Iterable<OpenFlowSwitch> getMasterSwitches() {
return activeMasterSwitches.values();
}
@Override
public Iterable<OpenFlowSwitch> getEqualSwitches() {
return activeEqualSwitches.values();
}
@Override
public OpenFlowSwitch getSwitch(Dpid dpid) {
return connectedSwitches.get(dpid);
}
@Override
public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
return activeMasterSwitches.get(dpid);
}
@Override
public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
return activeEqualSwitches.get(dpid);
}
@Override
public void addListener(OpenFlowSwitchListener listener) {
if (!ofEventListener.contains(listener)) {
this.ofEventListener.add(listener);
}
}
@Override
public void removeListener(OpenFlowSwitchListener listener) {
this.ofEventListener.remove(listener);
}
@Override
public void addPacketListener(int priority, PacketListener listener) {
ofPacketListener.add(priority, listener);
}
@Override
public void removePacketListener(PacketListener listener) {
ofPacketListener.remove(listener);
}
@Override
public void write(Dpid dpid, OFMessage msg) {
this.getSwitch(dpid).sendMsg(msg);
}
@Override
public void processPacket(Dpid dpid, OFMessage msg) {
switch (msg.getType()) {
case PORT_STATUS:
for (OpenFlowSwitchListener l : ofEventListener) {
l.portChanged(dpid, (OFPortStatus) msg);
}
break;
case PACKET_IN:
for (PacketListener p : ofPacketListener) {
//TODO fix me!
p.handlePacket(null);
}
break;
default:
log.warn("Handling message type {} not yet implemented", msg.getType());
}
}
@Override
public void setRole(Dpid dpid, RoleState role) {
getSwitch(dpid).setRole(role);
}
/**
* Implementation of an OpenFlow Agent which is responsible for
* keeping track of connected switches and the state in which
* they are.
*/
public class OpenFlowSwitchAgent implements OpenFlowAgent {
private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
private final Lock switchLock = new ReentrantLock();
@Override
public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
if (connectedSwitches.get(dpid) != null) {
log.error("Trying to add connectedSwitch but found a previous "
+ "value for dpid: {}", dpid);
return false;
} else {
log.error("Added switch {}", dpid);
connectedSwitches.put(dpid, sw);
for (OpenFlowSwitchListener l : ofEventListener) {
l.switchAdded(dpid);
}
return true;
}
}
@Override
public boolean validActivation(Dpid dpid) {
if (connectedSwitches.get(dpid) == null) {
log.error("Trying to activate switch but is not in "
+ "connected switches: dpid {}. Aborting ..",
dpid);
return false;
}
if (activeMasterSwitches.get(dpid) != null ||
activeEqualSwitches.get(dpid) != null) {
log.error("Trying to activate switch but it is already "
+ "activated: dpid {}. Found in activeMaster: {} "
+ "Found in activeEqual: {}. Aborting ..", new Object[]{
dpid,
(activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
(activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
return false;
}
return true;
}
@Override
public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
switchLock.lock();
try {
if (!validActivation(dpid)) {
return false;
}
activeMasterSwitches.put(dpid, sw);
return true;
} finally {
switchLock.unlock();
}
}
@Override
public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
switchLock.lock();
try {
if (!validActivation(dpid)) {
return false;
}
activeEqualSwitches.put(dpid, sw);
log.info("Added Activated EQUAL Switch {}", dpid);
return true;
} finally {
switchLock.unlock();
}
}
@Override
public void transitionToMasterSwitch(Dpid dpid) {
switchLock.lock();
try {
if (activeMasterSwitches.containsKey(dpid)) {
return;
}
OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
if (sw == null) {
log.error("Transition to master called on sw {}, but switch "
+ "was not found in controller-cache", dpid);
return;
}
log.info("Transitioned switch {} to MASTER", dpid);
activeMasterSwitches.put(dpid, sw);
} finally {
switchLock.unlock();
}
}
@Override
public void transitionToEqualSwitch(Dpid dpid) {
switchLock.lock();
try {
if (activeEqualSwitches.containsKey(dpid)) {
return;
}
OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
if (sw == null) {
log.error("Transition to equal called on sw {}, but switch "
+ "was not found in controller-cache", dpid);
return;
}
log.info("Transitioned switch {} to EQUAL", dpid);
activeEqualSwitches.put(dpid, sw);
} finally {
switchLock.unlock();
}
}
@Override
public void removeConnectedSwitch(Dpid dpid) {
connectedSwitches.remove(dpid);
OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
if (sw == null) {
sw = activeEqualSwitches.remove(dpid);
}
for (OpenFlowSwitchListener l : ofEventListener) {
l.switchRemoved(dpid);
}
}
@Override
public void processMessage(Dpid dpid, OFMessage m) {
processPacket(dpid, m);
}
}
protected List<PacketListener> ofPacketListener =
new ArrayList<>();
private final Controller ctrl = new Controller();
@Activate
public void activate() {
ctrl.start(agent);
}
@Deactivate
public void deactivate() {
ctrl.stop();
}
@Override
public Iterable<OpenFlowSwitch> getSwitches() {
return connectedSwitches.values();
}
@Override
public Iterable<OpenFlowSwitch> getMasterSwitches() {
return activeMasterSwitches.values();
}
@Override
public Iterable<OpenFlowSwitch> getEqualSwitches() {
return activeEqualSwitches.values();
}
@Override
public OpenFlowSwitch getSwitch(Dpid dpid) {
return connectedSwitches.get(dpid);
}
@Override
public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
return activeMasterSwitches.get(dpid);
}
@Override
public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
return activeEqualSwitches.get(dpid);
}
@Override
public void addListener(OpenFlowSwitchListener listener) {
if (!ofEventListener.contains(listener)) {
this.ofEventListener.add(listener);
}
}
@Override
public void removeListener(OpenFlowSwitchListener listener) {
this.ofEventListener.remove(listener);
}
@Override
public void addPacketListener(int priority, PacketListener listener) {
ofPacketListener.add(priority, listener);
}
@Override
public void removePacketListener(PacketListener listener) {
ofPacketListener.remove(listener);
}
@Override
public void write(Dpid dpid, OFMessage msg) {
this.getSwitch(dpid).sendMsg(msg);
}
@Override
public void processPacket(Dpid dpid, OFMessage msg) {
switch (msg.getType()) {
case PORT_STATUS:
for (OpenFlowSwitchListener l : ofEventListener) {
l.portChanged(dpid, (OFPortStatus) msg);
}
break;
case PACKET_IN:
for (PacketListener p : ofPacketListener) {
//TODO fix me!
p.handlePacket(DefaultPacketContext
.packetContextFromPacketIn(this.getSwitch(dpid),
(OFPacketIn) msg));
}
break;
default:
log.warn("Handling message type {} not yet implemented {}",
msg.getType(), msg);
}
}
@Override
public void setRole(Dpid dpid, RoleState role) {
getSwitch(dpid).setRole(role);
}
/**
* Implementation of an OpenFlow Agent which is responsible for
* keeping track of connected switches and the state in which
* they are.
*/
public class OpenFlowSwitchAgent implements OpenFlowAgent {
private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
private final Lock switchLock = new ReentrantLock();
@Override
public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
if (connectedSwitches.get(dpid) != null) {
log.error("Trying to add connectedSwitch but found a previous "
+ "value for dpid: {}", dpid);
return false;
} else {
log.error("Added switch {}", dpid);
connectedSwitches.put(dpid, sw);
for (OpenFlowSwitchListener l : ofEventListener) {
l.switchAdded(dpid);
}
return true;
}
}
@Override
public boolean validActivation(Dpid dpid) {
if (connectedSwitches.get(dpid) == null) {
log.error("Trying to activate switch but is not in "
+ "connected switches: dpid {}. Aborting ..",
dpid);
return false;
}
if (activeMasterSwitches.get(dpid) != null ||
activeEqualSwitches.get(dpid) != null) {
log.error("Trying to activate switch but it is already "
+ "activated: dpid {}. Found in activeMaster: {} "
+ "Found in activeEqual: {}. Aborting ..", new Object[]{
dpid,
(activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
(activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y'});
return false;
}
return true;
}
@Override
public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
switchLock.lock();
try {
if (!validActivation(dpid)) {
return false;
}
activeMasterSwitches.put(dpid, sw);
return true;
} finally {
switchLock.unlock();
}
}
@Override
public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
switchLock.lock();
try {
if (!validActivation(dpid)) {
return false;
}
activeEqualSwitches.put(dpid, sw);
log.info("Added Activated EQUAL Switch {}", dpid);
return true;
} finally {
switchLock.unlock();
}
}
@Override
public void transitionToMasterSwitch(Dpid dpid) {
switchLock.lock();
try {
if (activeMasterSwitches.containsKey(dpid)) {
return;
}
OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
if (sw == null) {
sw = getSwitch(dpid);
if (sw == null) {
log.error("Transition to master called on sw {}, but switch "
+ "was not found in controller-cache", dpid);
return;
}
}
log.info("Transitioned switch {} to MASTER", dpid);
activeMasterSwitches.put(dpid, sw);
} finally {
switchLock.unlock();
}
}
@Override
public void transitionToEqualSwitch(Dpid dpid) {
switchLock.lock();
try {
if (activeEqualSwitches.containsKey(dpid)) {
return;
}
OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
if (sw == null) {
log.error("Transition to equal called on sw {}, but switch "
+ "was not found in controller-cache", dpid);
return;
}
log.info("Transitioned switch {} to EQUAL", dpid);
activeEqualSwitches.put(dpid, sw);
} finally {
switchLock.unlock();
}
}
@Override
public void removeConnectedSwitch(Dpid dpid) {
connectedSwitches.remove(dpid);
OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
if (sw == null) {
sw = activeEqualSwitches.remove(dpid);
}
for (OpenFlowSwitchListener l : ofEventListener) {
l.switchRemoved(dpid);
}
}
@Override
public void processMessage(Dpid dpid, OFMessage m) {
processPacket(dpid, m);
}
}
}
......
......@@ -84,7 +84,7 @@ class RoleManager implements RoleHandler {
default:
// ensuring that the only two roles sent to 1.0 switches with
// Nicira role support, are MASTER and SLAVE
roleToSend = OFNiciraControllerRole.ROLE_SLAVE;
roleToSend = OFNiciraControllerRole.ROLE_OTHER;
log.warn("Sending Nx Role.SLAVE to switch {}.", sw);
}
int xid = sw.getNextTransactionId();
......
......@@ -50,8 +50,7 @@ public class OFSwitchImplOVS10 extends AbstractOpenFlowSwitch {
@Override
public void write(OFMessage msg) {
channel.write(msg);
channel.write(Collections.singletonList(msg));
}
@Override
......
......@@ -61,6 +61,14 @@
<version>18.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.9.0.Final</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
......
/*******************************************************************************
* Copyright 2014 Open Networking Laboratory
*
* 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 org.onlab.onos.provider.of.link.impl;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkDescription;
import org.onlab.onos.net.link.LinkProviderService;
import org.onlab.onos.of.controller.Dpid;
import org.onlab.onos.of.controller.OpenFlowController;
import org.onlab.onos.of.controller.OpenFlowSwitch;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ONLabLddp;
import org.onlab.packet.ONLabLddp.DPIDandPort;
import org.onlab.timer.Timer;
import org.projectfloodlight.openflow.protocol.OFFactory;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.slf4j.Logger;
/**
* Run discovery process from a physical switch. Ports are initially labeled as
* slow ports. When an LLDP is successfully received, label the remote port as
* fast. Every probeRate milliseconds, loop over all fast ports and send an
* LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
* discovery implementation.
*
* TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen
* for flow_removed messages
*/
public class LinkDiscovery implements TimerTask {
private final OpenFlowSwitch sw;
// send 1 probe every probeRate milliseconds
private final long probeRate;
private final Set<Integer> slowPorts;
private final Set<Integer> fastPorts;
// number of unacknowledged probes per port
private final Map<Integer, AtomicInteger> portProbeCount;
// number of probes to send before link is removed
private static final short MAX_PROBE_COUNT = 3;
private Iterator<Integer> slowIterator;
private final OFFactory ofFactory;
private final Logger log = getLogger(getClass());
private final ONLabLddp lldpPacket;
private final Ethernet ethPacket;
private Ethernet bddpEth;
private final boolean useBDDP;
private final OpenFlowController ctrl;
private final LinkProviderService linkProvider;
/**
* Instantiates discovery manager for the given physical switch. Creates a
* generic LLDP packet that will be customized for the port it is sent out on.
* Starts the the timer for the discovery process.
*
* @param sw the physical switch
* @param useBDDP flag to also use BDDP for discovery
*/
public LinkDiscovery(final OpenFlowSwitch sw,
OpenFlowController ctrl, LinkProviderService providerService, Boolean... useBDDP) {
this.sw = sw;
this.ofFactory = sw.factory();
this.ctrl = ctrl;
this.probeRate = 1000;
this.linkProvider = providerService;
this.slowPorts = Collections.synchronizedSet(new HashSet<Integer>());
this.fastPorts = Collections.synchronizedSet(new HashSet<Integer>());
this.portProbeCount = new HashMap<Integer, AtomicInteger>();
this.lldpPacket = new ONLabLddp();
this.lldpPacket.setSwitch(this.sw.getId());
this.ethPacket = new Ethernet();
this.ethPacket.setEtherType(Ethernet.TYPE_LLDP);
this.ethPacket.setDestinationMACAddress(ONLabLddp.LLDP_NICIRA);
this.ethPacket.setPayload(this.lldpPacket);
this.ethPacket.setPad(true);
this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
if (this.useBDDP) {
this.bddpEth = new Ethernet();
this.bddpEth.setPayload(this.lldpPacket);
this.bddpEth.setEtherType(Ethernet.TYPE_BSN);
this.bddpEth.setDestinationMACAddress(ONLabLddp.BDDP_MULTICAST);
this.bddpEth.setPad(true);
log.info("Using BDDP to discover network");
}
for (OFPortDesc port : sw.getPorts()) {
if (port.getPortNo() != OFPort.LOCAL) {
addPort(port);
}
}
Timer.getTimer().newTimeout(this, this.probeRate,
TimeUnit.MILLISECONDS);
this.log.debug("Started discovery manager for switch {}",
sw.getId());
}
/**
* Add physical port port to discovery process.
* Send out initial LLDP and label it as slow port.
*
* @param port the port
*/
public void addPort(final OFPortDesc port) {
// Ignore ports that are not on this switch, or already booted. */
synchronized (this) {
this.log.debug("sending init probe to port {}",
port.getPortNo().getPortNumber());
OFPacketOut pkt;
pkt = this.createLLDPPacketOut(port);
this.sw.sendMsg(pkt);
if (useBDDP) {
OFPacketOut bpkt = this.createBDDPPacketOut(port);
this.sw.sendMsg(bpkt);
}
this.slowPorts.add(port.getPortNo().getPortNumber());
this.slowIterator = this.slowPorts.iterator();
}
}
/**
* Removes physical port from discovery process.
*
* @param port the port
*/
public void removePort(final OFPort port) {
// Ignore ports that are not on this switch
int portnum = port.getPortNumber();
synchronized (this) {
if (this.slowPorts.contains(portnum)) {
this.slowPorts.remove(portnum);
this.slowIterator = this.slowPorts.iterator();
} else if (this.fastPorts.contains(portnum)) {
this.fastPorts.remove(portnum);
this.portProbeCount.remove(portnum);
// no iterator to update
} else {
this.log.warn(
"tried to dynamically remove non-existing port {}",
portnum);
}
}
}
/**
* Method called by remote port to acknowledge receipt of LLDP sent by
* this port. If slow port, updates label to fast. If fast port, decrements
* number of unacknowledged probes.
*
* @param port the port
*/
public void ackProbe(final Integer port) {
final int portNumber = port;
synchronized (this) {
if (this.slowPorts.contains(portNumber)) {
this.log.debug("Setting slow port to fast: {}:{}",
this.sw.getId(), portNumber);
this.slowPorts.remove(portNumber);
this.slowIterator = this.slowPorts.iterator();
this.fastPorts.add(portNumber);
this.portProbeCount.put(portNumber, new AtomicInteger(0));
} else {
if (this.fastPorts.contains(portNumber)) {
this.portProbeCount.get(portNumber).set(0);
} else {
this.log.debug(
"Got ackProbe for non-existing port: {}",
portNumber);
}
}
}
}
/**
* Creates packet_out LLDP for specified output port.
*
* @param port the port
* @return Packet_out message with LLDP data
* @throws PortMappingException
*/
private OFPacketOut createLLDPPacketOut(final OFPortDesc port) {
OFPacketOut.Builder packetOut = this.ofFactory.buildPacketOut();
packetOut.setBufferId(OFBufferId.NO_BUFFER);
OFAction act = this.ofFactory.actions().buildOutput()
.setPort(port.getPortNo()).build();
packetOut.setActions(Collections.singletonList(act));
this.lldpPacket.setPort(port.getPortNo().getPortNumber());
this.ethPacket.setSourceMACAddress(port.getHwAddr().getBytes());
final byte[] lldp = this.ethPacket.serialize();
packetOut.setData(lldp);
return packetOut.build();
}
/**
* Creates packet_out BDDP for specified output port.
*
* @param port the port
* @return Packet_out message with LLDP data
* @throws PortMappingException
*/
private OFPacketOut createBDDPPacketOut(final OFPortDesc port) {
OFPacketOut.Builder packetOut = sw.factory().buildPacketOut();
packetOut.setBufferId(OFBufferId.NO_BUFFER);
OFActionOutput.Builder act = sw.factory().actions().buildOutput()
.setPort(port.getPortNo());
OFAction out = act.build();
packetOut.setActions(Collections.singletonList(out));
this.lldpPacket.setPort(port.getPortNo().getPortNumber());
this.bddpEth.setSourceMACAddress(port.getHwAddr().getBytes());
final byte[] bddp = this.bddpEth.serialize();
packetOut.setData(bddp);
return packetOut.build();
}
private void sendMsg(final OFMessage msg) {
this.sw.sendMsg(msg);
}
public String getName() {
return "LinkDiscovery " + this.sw.getStringId();
}
/*
* Handles an incoming LLDP packet. Creates link in topology and sends ACK
* to port where LLDP originated.
*/
@SuppressWarnings("rawtypes")
public void handleLLDP(final Ethernet eth, Integer inPort) {
final byte[] pkt = eth.serialize();
if (ONLabLddp.isOVXLLDP(pkt)) {
final Integer dstPort = inPort;
final DPIDandPort dp = ONLabLddp.parseLLDP(pkt);
final OpenFlowSwitch srcSwitch = ctrl.getSwitch(new Dpid(dp.getDpid()));
final Integer srcPort = dp.getPort();
if (srcSwitch == null) {
return;
}
this.ackProbe(srcPort);
ConnectPoint src = new ConnectPoint(
DeviceId.deviceId("of:" + Long.toHexString(srcSwitch.getId())),
PortNumber.portNumber(srcPort));
ConnectPoint dst = new ConnectPoint(
DeviceId.deviceId("of:" + Long.toHexString(sw.getId())),
PortNumber.portNumber(dstPort));
LinkDescription ld;
if (eth.getEtherType() == Ethernet.TYPE_BSN) {
ld = new DefaultLinkDescription(src, dst, Type.INDIRECT);
} else {
ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
}
linkProvider.linkDetected(ld);
} else {
this.log.debug("Ignoring unknown LLDP");
}
}
private OFPortDesc findPort(List<OFPortDesc> ports, Integer inPort) {
for (OFPortDesc p : ports) {
if (p.getPortNo().getPortNumber() == inPort) {
return p;
}
}
return null;
}
/**
* Execute this method every t milliseconds. Loops over all ports
* labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
* port.
*
* @param t timeout
* @throws Exception
*/
@Override
public void run(final Timeout t) {
this.log.debug("sending probes");
synchronized (this) {
final Iterator<Integer> fastIterator = this.fastPorts.iterator();
while (fastIterator.hasNext()) {
final Integer portNumber = fastIterator.next();
final int probeCount = this.portProbeCount.get(portNumber)
.getAndIncrement();
OFPortDesc port = findPort(this.sw.getPorts(), portNumber);
if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
this.log.debug("sending fast probe to port");
OFPacketOut pkt = this.createLLDPPacketOut(port);
this.sendMsg(pkt);
if (useBDDP) {
OFPacketOut bpkt = this.createBDDPPacketOut(port);
this.sendMsg(bpkt);
}
} else {
// Update fast and slow ports
fastIterator.remove();
this.slowPorts.add(portNumber);
this.slowIterator = this.slowPorts.iterator();
this.portProbeCount.remove(portNumber);
// Remove link from topology
final OFPortDesc srcPort = port;
ConnectPoint cp = new ConnectPoint(
DeviceId.deviceId("of:" + Long.toHexString(sw.getId())),
PortNumber.portNumber(srcPort.getPortNo().getPortNumber()));
linkProvider.linksVanished(cp);
}
}
// send a probe for the next slow port
if (this.slowPorts.size() > 0) {
if (!this.slowIterator.hasNext()) {
this.slowIterator = this.slowPorts.iterator();
}
if (this.slowIterator.hasNext()) {
final int portNumber = this.slowIterator.next();
this.log.debug("sending slow probe to port {}", portNumber);
OFPortDesc port = findPort(this.sw.getPorts(), portNumber);
OFPacketOut pkt = this.createLLDPPacketOut(port);
this.sendMsg(pkt);
if (useBDDP) {
OFPacketOut bpkt = this.createBDDPPacketOut(port);
this.sendMsg(bpkt);
}
}
}
}
// reschedule timer
Timer.getTimer().newTimeout(this, this.probeRate,
TimeUnit.MILLISECONDS);
}
public void removeAllPorts() {
for (OFPortDesc port : sw.getPorts()) {
removePort(port.getPortNo());
}
}
}
......@@ -2,11 +2,17 @@ package org.onlab.onos.provider.of.link.impl;
import static org.slf4j.LoggerFactory.getLogger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.link.LinkProvider;
import org.onlab.onos.net.link.LinkProviderRegistry;
import org.onlab.onos.net.link.LinkProviderService;
......@@ -17,6 +23,10 @@ import org.onlab.onos.of.controller.OpenFlowController;
import org.onlab.onos.of.controller.OpenFlowSwitchListener;
import org.onlab.onos.of.controller.PacketContext;
import org.onlab.onos.of.controller.PacketListener;
import org.onlab.timer.Timer;
import org.projectfloodlight.openflow.protocol.OFPortConfig;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.OFPortState;
import org.projectfloodlight.openflow.protocol.OFPortStatus;
import org.slf4j.Logger;
......@@ -37,6 +47,8 @@ public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvid
private LinkProviderService providerService;
private final boolean useBDDP = true;
private final InternalLinkProvider listener = new InternalLinkProvider();
/**
......@@ -60,32 +72,61 @@ public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvid
controller.removeListener(listener);
controller.removePacketListener(listener);
providerService = null;
Timer.getTimer().stop();
log.info("Stopped");
}
private class InternalLinkProvider implements PacketListener, OpenFlowSwitchListener {
private final Map<Dpid, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
@Override
public void handlePacket(PacketContext pktCtx) {
LinkDiscovery ld = discoverers.get(pktCtx.dpid());
if (ld == null) {
return;
}
ld.handleLLDP(pktCtx.parsed(),
pktCtx.inPort());
}
@Override
public void switchAdded(Dpid dpid) {
// TODO Auto-generated method stub
discoverers.put(dpid, new LinkDiscovery(controller.getSwitch(dpid),
controller, providerService, useBDDP));
}
@Override
public void switchRemoved(Dpid dpid) {
// TODO Auto-generated method stub
LinkDiscovery ld = this.discoverers.remove(dpid);
if (ld != null) {
ld.removeAllPorts();
}
providerService.linksVanished(
DeviceId.deviceId("of:" + Long.toHexString(dpid.value())));
}
@Override
public void portChanged(Dpid dpid, OFPortStatus status) {
// TODO Auto-generated method stub
LinkDiscovery ld = discoverers.get(dpid);
if (ld == null) {
return;
}
final OFPortDesc port = status.getDesc();
final boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) &&
!port.getConfig().contains(OFPortConfig.PORT_DOWN);
if (enabled) {
ld.addPort(port);
} else {
ConnectPoint cp = new ConnectPoint(
DeviceId.deviceId("of:" + Long.toHexString(dpid.value())),
PortNumber.portNumber(port.getPortNo().getPortNumber()));
providerService.linksVanished(cp);
ld.removePort(port.getPortNo());
}
}
......
......@@ -25,7 +25,10 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty</artifactId>
<version>3.9.0.Final</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
</dependencies>
......
......@@ -21,6 +21,8 @@ import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
......@@ -31,6 +33,7 @@ import org.apache.commons.lang.ArrayUtils;
@SuppressWarnings("rawtypes")
public class ONLabLddp extends LLDP {
private static final Logger log = LoggerFactory.getLogger(ONLabLddp.class);
// ON.Lab OUI and OVX name for organizationally specific TLVs
public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
public static final String OVX_NAME = "OpenVirteX";
......@@ -50,7 +53,7 @@ public class ONLabLddp extends LLDP {
private static final byte CHASSIS_TLV_SUBTYPE = 4;
private static final byte PORT_TLV_TYPE = 2;
private static final byte PORT_TLV_SIZE = 7;
private static final byte PORT_TLV_SIZE = 5;
private static final byte PORT_TLV_SUBTYPE = 2;
private static final byte TTL_TLV_TYPE = 3;
......@@ -60,7 +63,7 @@ public class ONLabLddp extends LLDP {
// 4 = OUI (3) + subtype (1)
private static final byte NAME_TLV_SIZE = (byte) (4 + ONLabLddp.OVX_NAME.length());
private static final byte NAME_TLV_SUBTYPE = 1;
private static final short NAME_TLV_OFFSET = 32;
private static final short NAME_TLV_OFFSET = 34;
private static final short NAME_TLV_HEADER = (short) ((NAME_TLV_TYPE << 9) | NAME_TLV_SIZE);
// Contents of full name TLV
private static final byte[] NAME_TLV = ByteBuffer.allocate(NAME_TLV_SIZE + 2)
......@@ -85,7 +88,7 @@ public class ONLabLddp extends LLDP {
// Default switch, port number and TTL
private static final byte[] DEFAULT_DPID = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00 };
private static final short DEFAULT_PORT = 0;
private static final int DEFAULT_PORT = 0;
private static final short DEFAULT_TTL = 120; // in seconds
// Minimum and OVX-generated LLDP packet sizes
......@@ -97,7 +100,7 @@ public class ONLabLddp extends LLDP {
// Field offsets in OVX-generated LLDP
private static final short ETHERTYPE_OFFSET = 12;
private static final short PORT_OFFSET = 26;
private static final short DPID_OFFSET = 54;
private static final short DPID_OFFSET = 56;
// Private member fields
// Byte arrays for TLV information string
......@@ -167,10 +170,10 @@ public class ONLabLddp extends LLDP {
*
* @param portNumber the port number
*/
private void setPortTLV(final long portNumber) {
private void setPortTLV(final int portNumber) {
this.bb = ByteBuffer.wrap(this.portId);
this.bb.put(PORT_TLV_SUBTYPE);
this.bb.putLong(portNumber);
this.bb.putInt(portNumber);
this.portTLV.setLength(PORT_TLV_SIZE);
this.portTLV.setType(PORT_TLV_TYPE);
......@@ -240,8 +243,8 @@ public class ONLabLddp extends LLDP {
*
* @param port the port instance
*/
public void setPort(long port) {
long portNumber = port;
public void setPort(int port) {
int portNumber = port;
this.setPortTLV(portNumber);
}
......@@ -335,7 +338,7 @@ public class ONLabLddp extends LLDP {
* @param packet
* @return Dpid and port
*/
/* public static long parseLLDP(final byte[] packet) {
public static DPIDandPort parseLLDP(final byte[] packet) {
final ByteBuffer bb = ByteBuffer.wrap(packet);
// Extra offset due to VLAN tag
......@@ -345,10 +348,30 @@ public class ONLabLddp extends LLDP {
offset = 4;
}
final short port = bb.getLong(PORT_OFFSET + offset);
final int port = bb.getInt(PORT_OFFSET + offset);
final long dpid = bb.getLong(DPID_OFFSET + offset);
return dpid
return new DPIDandPort(dpid, port);
}
*/
public static class DPIDandPort {
private final long dpid;
private final int port;
public DPIDandPort(long dpid, int port) {
this.dpid = dpid;
this.port = port;
}
public long getDpid() {
return this.dpid;
}
public int getPort() {
return this.port;
}
}
}
......
package org.onlab.timer;
import org.jboss.netty.util.HashedWheelTimer;
public final class Timer {
private Timer() {}
private static HashedWheelTimer timer;
public static HashedWheelTimer getTimer() {
if (Timer.timer == null) {
Timer.timer = new HashedWheelTimer();
Timer.timer.start();
}
return Timer.timer;
}
}