/*
* Copyright (c) 2013 Big Switch Networks, Inc.
*
* Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html
*
* 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.sdnplatform.topology;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import org.openflow.protocol.OFFlowRemoved;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.util.HexString;
import org.sdnplatform.core.ListenerContext;
import org.sdnplatform.core.IControllerService;
import org.sdnplatform.core.IOFSwitch;
import org.sdnplatform.core.module.ModuleContext;
import org.sdnplatform.core.module.ModuleException;
import org.sdnplatform.core.module.IModule;
import org.sdnplatform.core.module.IPlatformService;
import org.sdnplatform.linkdiscovery.ILinkDiscoveryListener;
import org.sdnplatform.linkdiscovery.ILinkDiscoveryService;
import org.sdnplatform.restserver.IRestApiService;
import org.sdnplatform.routing.IRoutingService;
import org.sdnplatform.routing.Link;
import org.sdnplatform.routing.Route;
import org.sdnplatform.threadpool.IThreadPoolService;
import org.sdnplatform.topology.TunnelEvent.TunnelLinkStatus;
import org.sdnplatform.topology.web.TopologyWebRoutable;
import org.sdnplatform.tunnelmanager.ITunnelManagerListener;
import org.sdnplatform.tunnelmanager.ITunnelManagerService;
import org.sdnplatform.vendor.OFActionTunnelDstIP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BetterTopologyManager extends TopologyManager
implements IModule, IBetterTopologyService, IRoutingService,
ILinkDiscoveryListener, ITunnelManagerListener {
protected static final Logger log =
LoggerFactory.getLogger(BetterTopologyManager.class);
ITunnelManagerService tunnelManager = null;
Set<BroadcastDomain> broadcastDomains;
Long tunnelDomain;
// Declarations for tunnel liveness detection
// The ordered node pairs here are switch DPIDs.
/**
* Queue to detect tunnel failures. The queue consists of ordered nodepair
* (srcDPID, dstDPID) that's created whenever we see a flow-mod that is
* pushed to a switch where the flow starts from the tunnel loopback port.
* When a flow-mod is pushed to a switch that ends at a tunnel loopback
* port, we remove the nodepair. If we do not receive the corresponding
* flow-mod that ends at tunnel loopback, it is an indication of a potential
* tunnel failure. To validate this, we send LLDP on the tunnel ports,
* and add the tunnel endpoints to the verification queue. If the LLDP is
* recieved within a certain time interval, then the tunnel assumed to be
* alive. Otherwise, then the tunnel is assumed to have failed. All tunnel
* failures are logged as warning messages. The status queue is cleaned
* every 60 seconds and may be accessed through the CLI.
*/
BlockingQueue<OrderedNodePair> tunnelDetectionQueue;
int TUNNEL_DETECTION_TIMEOUT_MS = 2000; // 2 seconds
int tunnelDetectionTime = TUNNEL_DETECTION_TIMEOUT_MS;
/**
* This queue holds tunnel events for which an LLDP has been sent
* and waiting for the LLDP to arrive at the other end of the tunnel.
*/
BlockingQueue<OrderedNodePair> tunnelVerificationQueue;
int TUNNEL_VERIFICATION_TIMEOUT_MS = 2000; // 2 seconds
int tunnelVerificationTime = TUNNEL_VERIFICATION_TIMEOUT_MS;
/**
* This queue holds the tunnel events for which LLDP was sent, but
* the LLDP was not received within the desired time.
*/
ConcurrentHashMap<Long, ConcurrentHashMap<Long, TunnelEvent>> tunnelStatusMap;
// *****************
// Topology Computation
// *****************
/** This is the main entry point into the creation of a new topology
* given a link discovery update or other events such as enabling/disabling
* multipath, HA etc. It is important to note that just because there is a
* link update, topology need not necessarily be recomputed. For example,
* broadcast links continue to appear/disappear given our BDDP probing mechanism;
* but the set of broadcast ports (that make up the broadcast domain) stays
* the same (in stable conditions) - in these situations topology is not
* recomputed and new instances are not formed.
*/
@Override
public boolean createNewInstance() {
boolean recomputeTopologyFlag = (dtLinksUpdated || tunnelPortsUpdated);
// Create a new tunnel domain. If the tunnel domain identifier
// is different from the previous one, topology needs to be
// recomputed. We may optimize this logic by checking only
// if switches were added/updated.
Long newTunnelDomain = new Long(createTunnelDomain());
if (tunnelDomain == null || !tunnelDomain.equals(newTunnelDomain)) {
tunnelDomain = newTunnelDomain;
recomputeTopologyFlag = true;
log.trace("Topology recomputed due to tunnel domain id change.");
}
Set<NodePortTuple> blockedPorts = new HashSet<NodePortTuple>();
Map<NodePortTuple, Set<Link>> linksWithoutTunnels;
linksWithoutTunnels =
new HashMap<NodePortTuple, Set<Link>>(switchPortLinks);
Set<NodePortTuple> broadcastDomainPorts =
identifyBroadcastDomainPorts();
Set<BroadcastDomain> bDomains =
identifyNonOpenflowDomains(broadcastDomainPorts);
if (broadcastDomains == null ||
broadcastDomains.equals(bDomains) == false) {
broadcastDomains = bDomains;
recomputeTopologyFlag = true;
}
// Quit if topology re-computation is not necessary.
if (!recomputeTopologyFlag) return false;
// Remove tunnel links
for (NodePortTuple npt: tunnelPorts) {
linksWithoutTunnels.remove(npt);
}
// First, create a topology instance excluding the tunnel links.
// There could be a few blocked links, hence ports. Get the
// collection of blocked ports from this topology instance.
//
// ignore tunnel ports.
BetterTopologyInstance ntNoTunnels =
new BetterTopologyInstance(switchPorts,
blockedPorts,
linksWithoutTunnels,
broadcastDomainPorts,
new HashSet<NodePortTuple>(), // no tunnel ports
broadcastDomains,
tunnelDomain,
controllerProvider,
tunnelManager);
ntNoTunnels.compute();
// Now including the tunnel ports
BetterTopologyInstance nt =
new BetterTopologyInstance(switchPorts,
blockedPorts,
switchPortLinks,
broadcastDomainPorts,
tunnelPorts,
broadcastDomains,
tunnelDomain,
controllerProvider,
tunnelManager);
nt.compute();
currentInstanceWithoutTunnels = ntNoTunnels;
currentInstance = nt;
return true;
}
/**
* @author Srinivasan Ramasubramanian
*
* This function computes the groups of
* switch ports that connect to non-openflow domains. They are
* grouped together based on whether there's a broadcast
* leak from one to another or not. We will currently
* ignore directionality of the links from the switch ports.
*
* Assuming link tuple as undirected, the goal is to simply
* compute connected components.
*
*/
protected Set<BroadcastDomain>
identifyNonOpenflowDomains(Set<NodePortTuple> broadcastDomainPorts) {
Set<BroadcastDomain> broadcastDomains = new HashSet<BroadcastDomain>();
Set<NodePortTuple> visitedNpt = new HashSet<NodePortTuple>();
// create an queue of NPT to be examined.
Queue<NodePortTuple> nptQueue = new LinkedList<NodePortTuple>();
// Do a breadth first search to get all the connected components
for(NodePortTuple npt: broadcastDomainPorts) {
if (visitedNpt.contains(npt)) continue;
BroadcastDomain bd = new BroadcastDomain();
bd.add(npt);
bd.setId(broadcastDomains.size()+1);
broadcastDomains.add(bd);
visitedNpt.add(npt);
nptQueue.add(npt);
while(nptQueue.peek() != null) {
NodePortTuple currNpt = nptQueue.remove();
if (switchPortLinks.containsKey(currNpt) == false) continue;
for(Link l: switchPortLinks.get(currNpt)) {
NodePortTuple otherNpt;
if (l.getSrc() == currNpt.getNodeId() &&
l.getSrcPort() == currNpt.getPortId()) {
otherNpt = new NodePortTuple(l.getDst(), l.getDstPort());
} else {
otherNpt = new NodePortTuple(l.getSrc(), l.getSrcPort());
}
if (visitedNpt.contains(otherNpt) == false) {
nptQueue.add(otherNpt);
visitedNpt.add(otherNpt);
bd.add(otherNpt);
}
}
}
}
if (broadcastDomains.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace("No broadcast domains exist.");
}
} else {
if (log.isTraceEnabled()) {
StringBuffer bds = new StringBuffer();
for(BroadcastDomain bd:broadcastDomains) {
bds.append(bd);
bds.append(" ");
}
log.trace("Broadcast domains found in the network: {}", bds);
}
}
return broadcastDomains;
}
/**
* Get the set of ports to eliminate for sending out BDDP. The method
* returns all the ports that are suppressed for link discovery on the
* switch and the tunnel port on the switch.
* @param sid
* @return
*/
@Override
protected Set<Short> getPortsToEliminateForBDDP(long sid) {
Set<NodePortTuple> suppressedNptList = linkDiscovery.getSuppressLLDPsInfo();
if (suppressedNptList == null) return null;
Set<Short> resultPorts = new HashSet<Short>();
for(NodePortTuple npt: suppressedNptList) {
if (npt.getNodeId() == sid) {
resultPorts.add(npt.getPortId());
}
}
Short tunnelPort = tunnelManager.getTunnelPortNumber(sid);
if (tunnelPort != null)
resultPorts.add(tunnelPort);
return resultPorts;
}
/**
* We model tunnel domain as a switch. In order to assign a
* DPID to the switch, we start with a DPID value and check if
* it is already part of the regular set of switches. If so,
* we increment and check again. This procedure continues
* until we are able to assign a DPID for the tunnel domain
* switch.
*/
private long createTunnelDomain() {
long tid = 0x00FFFFFFFFFFFFFFL;
if (controllerProvider.getSwitches() != null) {
Set<Long> switches = controllerProvider.getSwitches().keySet();
// Get an id for the tunnel domain such that that id does not
// correspond to an already existing switch.
while(switches.contains(tid)) {
tid++;
}
}
return tid;
}
/**
* Return tunnel domain identifier.
* @return
*/
public Long getTunnelDomainId() {
return new Long(tunnelDomain);
}
// ghetto hax
@Override
public BetterTopologyInstance getCurrentInstance(boolean tunnelEnabled) {
if (tunnelEnabled)
return (BetterTopologyInstance)currentInstance;
else return (BetterTopologyInstance)this.currentInstanceWithoutTunnels;
}
@Override
public BetterTopologyInstance getCurrentInstance() {
return this.getCurrentInstance(true);
}
@Override
public void init(ModuleContext context)
throws ModuleException {
super.init(context);
tunnelManager = context.getServiceImpl(ITunnelManagerService.class);
this.tunnelDetectionQueue = new LinkedBlockingQueue<OrderedNodePair>();
this.tunnelVerificationQueue = new LinkedBlockingQueue<OrderedNodePair>();
this.tunnelStatusMap = new ConcurrentHashMap<Long,
ConcurrentHashMap<Long, TunnelEvent>>();
tunnelDetectionTime = this.TUNNEL_DETECTION_TIMEOUT_MS;
tunnelVerificationTime = this.TUNNEL_VERIFICATION_TIMEOUT_MS;
}
@Override
public void startUp(ModuleContext context) {
super.startUp(context);
if (tunnelManager != null) {
tunnelManager.addListener(this);
} else {
log.warn("Cannot listen to tunnel manager as the module is not loaded.");
}
}
// ***************
// IRoutingService
// ***************
@Override
public ArrayList<Route> getRoutes(long srcDpid, long dstDpid, boolean tunnelEnabled) {
// return multipath routes
BetterTopologyInstance ti = getCurrentInstance(tunnelEnabled);
return ti.getRoutes(srcDpid, dstDpid);
}
// *****************
// IModule
// *****************
@Override
public Collection<Class<? extends IPlatformService>> getModuleServices() {
Collection<Class<? extends IPlatformService>> l =
new ArrayList<Class<? extends IPlatformService>>();
l.add(ITopologyService.class);
l.add(IBetterTopologyService.class);
l.add(IRoutingService.class);
return l;
}
@Override
public Map<Class<? extends IPlatformService>, IPlatformService>
getServiceImpls() {
Map<Class<? extends IPlatformService>,
IPlatformService> m =
new HashMap<Class<? extends IPlatformService>,
IPlatformService>();
// We are the class that implements the service
m.put(ITopologyService.class, this);
m.put(IBetterTopologyService.class, this);
m.put(IRoutingService.class, this);
return m;
}
@Override
public Collection<Class<? extends IPlatformService>>
getModuleDependencies() {
Collection<Class<? extends IPlatformService>> l =
new ArrayList<Class<? extends IPlatformService>>();
l.add(ILinkDiscoveryService.class);
l.add(IThreadPoolService.class);
l.add(IControllerService.class);
l.add(IRestApiService.class);
l.add(ITunnelManagerService.class);
return l;
}
@Override
protected void addRestletRoutable() {
restApi.addRestletRoutable(new TopologyWebRoutable());
}
// *****************
// IBetterTopologyService
// *****************
@Override
public Set<BroadcastDomain> getBroadcastDomains() {
return getCurrentInstance(true).getBroadcastDomains();
}
@Override
public Map<Long, Object> getHigherTopologyNodes() {
return getCurrentInstance(true).getHTNodes();
}
@Override
public Map<Long, Set<Long>> getHigherTopologyNeighbors() {
return getCurrentInstance(true).getHTNeighbors();
}
@Override
public Map<Long, Map<Long, Long>> getHigherTopologyNextHops() {
return getCurrentInstance(true).getHTNextHops();
}
@Override
public Map<Long, Long> getL2DomainIds() {
return getCurrentInstance(true).getL2DomainIds();
}
@Override
public Map<OrderedNodePair, Set<NodePortTuple>> getAllowedUnicastPorts() {
return getCurrentInstance(true).getAllowedPorts();
}
@Override
public Map<OrderedNodePair, NodePortTuple> getAllowedIncomingBroadcastPorts() {
return getCurrentInstance(true).getAllowedIncomingBroadcastPorts();
}
@Override
public Map<NodePortTuple, Set<Long>> getAllowedPortToBroadcastDomains() {
return getCurrentInstance(true).getAllowedPortsToBroadcastDomains();
}
// *****************
// Tunnel liveness methods and IBetterTopology Service
// *****************
private void addToTunnelStatus(TunnelEvent event) {
long src = event.getSrcDPID();
long dst = event.getDstDPID();
// Only add to tunnel status if the switch is
// known and is in active state. This is to ensure that
// we will have a valid event in the future that would
// allow us to remove this entry from the status queue.
if (controllerProvider.getSwitches().get(src) == null ||
tunnelManager.isTunnelActiveByDpid(src) == false ||
controllerProvider.getSwitches().get(dst) == null ||
tunnelManager.isTunnelActiveByDpid(dst) == false)
return;
tunnelStatusMap.putIfAbsent(src,
new ConcurrentHashMap<Long, TunnelEvent>());
tunnelStatusMap.get(src).put(dst, event);
tunnelStatusMap.putIfAbsent(dst,
new ConcurrentHashMap<Long, TunnelEvent>());
tunnelStatusMap.get(dst).put(src, event);
}
private void removeFromTunnelStatus(OrderedNodePair onp) {
long src = onp.getSrc();
long dst = onp.getDst();
ConcurrentHashMap<Long, TunnelEvent> map;
map = tunnelStatusMap.get(src);
if (map != null) {
map.remove(dst);
}
map = tunnelStatusMap.get(dst);
if (map != null) {
map.remove(src);
}
}
private void removeFromTunnelStatus(long sw) {
tunnelStatusMap.remove(sw);
for(Long othersw: tunnelStatusMap.keySet()) {
tunnelStatusMap.get(othersw).remove(sw);
}
}
/**
* The tunnel liveness detection and verification logic works as part
* of the miscellaneous periodic events.
*/
@Override
protected void handleMiscellaneousPeriodicEvents() {
tunnelDetectionTime -= TOPOLOGY_COMPUTE_INTERVAL_MS;
tunnelVerificationTime -= TOPOLOGY_COMPUTE_INTERVAL_MS;
if (tunnelVerificationTime <= 0) {
tunnelVerificationTime = this.TUNNEL_VERIFICATION_TIMEOUT_MS;
Set<OrderedNodePair> npSet = new HashSet<OrderedNodePair>();
npSet.addAll(tunnelVerificationQueue);
tunnelVerificationQueue.clear();
if (!npSet.isEmpty()) {
for (OrderedNodePair onp: npSet) {
TunnelEvent event = new TunnelEvent(onp.getSrc(),
onp.getDst(),
TunnelLinkStatus.DOWN);
log.warn("Tunnel link failed. src-dpid: {}, dst-dpid: {}",
HexString.toHexString(onp.getSrc()),
HexString.toHexString(onp.getDst()));
addToTunnelStatus(event);
}
}
}
if (tunnelDetectionTime <= 0) {
tunnelDetectionTime = this.TUNNEL_DETECTION_TIMEOUT_MS;
while (tunnelDetectionQueue.peek() != null) {
OrderedNodePair onp = tunnelDetectionQueue.remove();
this.verifyTunnelLiveness(onp.getSrc(), onp.getDst());
}
}
}
public BlockingQueue<OrderedNodePair> getTunnelDetectionQueue() {
return tunnelDetectionQueue;
}
public BlockingQueue<OrderedNodePair> getTunnelVerificationQueue() {
return tunnelVerificationQueue;
}
/**
* When a tunnel link addorUpdate event occurs, we need to remove
* the corresponding tunnel from the tunnelDetectionQueue as we have
* detected the tunnel destination switch port.
*/
@Override
protected void addOrUpdateTunnelLink(long srcId, short srcPort, long dstId,
short dstPort) {
// Here, we need to remove the link from the check
// and add the status that the tunnel is up.
OrderedNodePair onp = new OrderedNodePair(srcId, dstId);
tunnelDetectionQueue.remove(onp);
tunnelVerificationQueue.remove(onp);
removeFromTunnelStatus(onp);
}
@Override
public void detectTunnelSource(long srcDPID, long dstDPID) {
OrderedNodePair onp = new OrderedNodePair(srcDPID, dstDPID);
// Add this event to the detection queue only if this event
// is not already in the detection or verification queues.
if (!tunnelVerificationQueue.contains(onp) &&
!tunnelDetectionQueue.contains(onp))
tunnelDetectionQueue.add(onp);
}
@Override
public void detectTunnelDestination(long srcDPID, long dstDPID) {
OrderedNodePair onp = new OrderedNodePair(srcDPID, dstDPID);
tunnelDetectionQueue.remove(onp);
tunnelVerificationQueue.remove(onp);
removeFromTunnelStatus(onp);
}
@Override
public void verifyTunnelOnDemand(long srcDPID, long dstDPID) {
detectTunnelSource(srcDPID, dstDPID);
detectTunnelSource(dstDPID, srcDPID);
}
private void verifyTunnelLiveness(long srcDPID, long dstDPID) {
if (tunnelManager == null) {
log.warn("Cannot veirfy tunnel without tunnel manager.");
return;
}
// If the tunnel end-points are not active, there's no point in
// verifying liveness.
if (!tunnelManager.isTunnelActiveByDpid(srcDPID)) {
if (log.isTraceEnabled()) {
log.trace("Switch {} is not in tunnel active state," +
" cannot verify tunnel liveness.", srcDPID);
}
return;
}
if (!tunnelManager.isTunnelActiveByDpid(dstDPID)) {
if (log.isTraceEnabled()) {
log.trace("Switch {} is not in tunnel active state," +
" cannot verify tunnel liveness.", dstDPID);
}
return;
}
// At this point, both endpoints are tunnel active.
Short srcPort = tunnelManager.getTunnelPortNumber(srcDPID);
Integer dstIpAddr = tunnelManager.getTunnelIPAddr(dstDPID);
IOFSwitch iofSwitch = controllerProvider.getSwitches().get(srcDPID);
if (iofSwitch == null) {
if (log.isTraceEnabled()) {
log.trace("Cannot send tunnel LLDP as switch object does " +
"not exist for DPID {}", srcDPID);
}
return;
}
// Generate and send an LLDP to the tunnel port of srcDPID
OFPacketOut po = linkDiscovery.generateLLDPMessage(srcDPID,
srcPort.shortValue(),
true, false);
List<OFAction> actions = new ArrayList<OFAction>();
short actionsLength = 0;
// Set the tunnel destination action
OFActionTunnelDstIP tunnelDstAction =
new OFActionTunnelDstIP(dstIpAddr.intValue());
actions.add(tunnelDstAction);
actionsLength += tunnelDstAction.getLengthU();
// Set the output port action
OFActionOutput outputAction = new OFActionOutput();
outputAction.setPort(srcPort.shortValue());
actions.add(outputAction);
actionsLength += outputAction.getLengthU();
po.setActions(actions);
po.setActionsLength(actionsLength);
po.setLengthU(po.getLengthU() + actionsLength);
try {
iofSwitch.write(po, null);
iofSwitch.flush();
// once the LLDP is written, add the tunnel event to the
// checkTunnelLivenessQueue
OrderedNodePair onp = new OrderedNodePair(srcDPID, dstDPID);
this.tunnelVerificationQueue.add(onp);
} catch (IOException e) {
log.error("Failure sending LLDP out port {} on switch {}",
new Object[] { srcPort, iofSwitch.getStringId() }, e);
}
}
@Override
public void clearTunnelLivenessState() {
this.tunnelStatusMap.clear();
}
/**
* Get all the tunnel events from the hashmap.
* There could be duplicates as the events are indexed based on
* src and dst DPIDs. So, it is first put into a set and then
* changed to a list.
*/
public List<TunnelEvent> getTunnelLivenessState() {
HashSet<TunnelEvent> eventSet = new HashSet<TunnelEvent>();
for(Long src: tunnelStatusMap.keySet()) {
ConcurrentHashMap<Long, TunnelEvent> map =
tunnelStatusMap.get(src);
if (map != null)
eventSet.addAll(map.values());
}
return new ArrayList<TunnelEvent>(eventSet);
}
/**
* Get the tunnel liveness state -- bidirectional -- between a given
* source destination pair.
*/
public List<TunnelEvent> getTunnelLivenessState(long srcDPID,
long dstDPID) {
List<TunnelEvent> eventList = new ArrayList<TunnelEvent>();
TunnelEvent event;
event = getTunnelLivenessStateDirectional(srcDPID, dstDPID);
eventList.add(event);
event = getTunnelLivenessStateDirectional(dstDPID, srcDPID);
eventList.add(event);
return eventList;
}
/**
* Get the status of a specific directed tunnel from
* srcDPID to dstDPID.
*/
private TunnelEvent getTunnelLivenessStateDirectional(long srcDPID,
long dstDPID) {
OrderedNodePair onp = new OrderedNodePair(srcDPID, dstDPID);
TunnelEvent event = null;
if (tunnelStatusMap.containsKey(onp.getSrc())) {
event = tunnelStatusMap.get(onp.getSrc()).get(onp.getDst());
}
if (event == null) {
// The tunnel event doesn't exist. This could either mean
// that one or both switch ports are not tunnel capable/active
// or the tunnel is up.
Short srcPort = tunnelManager.getTunnelPortNumber(srcDPID);
Short dstPort = tunnelManager.getTunnelPortNumber(dstDPID);
if (srcPort == null || dstPort == null) {
event = new TunnelEvent(srcDPID, dstDPID,
TunnelLinkStatus.NOT_ENABLED);
} else if (!tunnelManager.isTunnelActiveByDpid(srcDPID) ||
!tunnelManager.isTunnelActiveByDpid(dstDPID)) {
// As one or both of the endpoints is not in active
// state, tunnel link is in down state.
event = new TunnelEvent(srcDPID, dstDPID,
TunnelLinkStatus.DOWN);
}
else {
// If no entry is present and the tunnel endpoints are active
// then the tunnel is up.
event = new TunnelEvent(srcDPID, dstDPID,
TunnelLinkStatus.UP);
}
}
return event;
}
/**
* Get the timeout to detect if the last hop tunnel flowmod was received
* or not. The value is in milliseconds.
* @return
*/
public int geTunnelDetectionTimeout() {
return TUNNEL_DETECTION_TIMEOUT_MS;
}
/**
* Set the timetout to detect if the last hop tunnel flowmod was received
* or not. The value is in milliseconds.
* @param time_ms
*/
public void setTunnelDetectionTimeout(int time_ms) {
TUNNEL_DETECTION_TIMEOUT_MS = time_ms;
}
/**
* Get the timeout for LLDP reception on tunnel ports. The value is
* in milliseconds.
* @return
*/
public int getTunnelVerificationTimeout() {
return TUNNEL_VERIFICATION_TIMEOUT_MS;
}
/**
* Set the timeout for LLDP reception on tunnel ports. The value is
* in milliseconds.
* @param time_ms
*/
public void setTunnelVerificationTimeout(int time_ms) {
TUNNEL_VERIFICATION_TIMEOUT_MS = time_ms;
}
/**
* To use this method to finally check for tunnel status when flowmods
* are being removed.
*/
private Command flowRemoved(IOFSwitch sw, OFFlowRemoved msg,
ListenerContext cntx) {
/*
if (tunnelManager == null) return Command.CONTINUE;
OFMatch match = msg.getMatch();
long dpid = sw.getId();
int srcIp = match.getNetworkSource();
int dstIp = match.getNetworkDestination();
Long srcDPID = tunnelManager.getSwitchDpid(srcIp);
Long dstDPID = tunnelManager.getSwitchDpid(dstIp);
if (srcDPID != null && dstDPID != null && !srcDPID.equals(dstDPID)) {
if (srcDPID.equals(dpid)) {
// the traffic is from the tunnel IP to tunnel IP.
this.detectTunnelSource(srcDPID, dstDPID);
} else if (dstDPID.equals(dpid)) {
// the traffic is destined
this.detectTunnelDestination(srcDPID, dstDPID);
}
}
*/
return Command.CONTINUE;
}
@Override
public void removeTunnelPort(long sw, short port) {
NodePortTuple npt = new NodePortTuple(sw, port);
tunnelPorts.remove(npt);
tunnelPortsUpdated = true;
// This call is not present in TopologyManager
removeFromTunnelStatus(sw);
}
// *****************
// ITunnelManagerListener methods
// *****************
@Override
public void tunnelPortActive(long dpid, short tunnelPortNumber) {
LDUpdate ldupdate = new LDUpdate(dpid, tunnelPortNumber,
UpdateOperation.TUNNEL_PORT_ADDED);
ldUpdates.add(ldupdate);
}
@Override
public void tunnelPortInactive(long dpid, short tunnelPortNumber) {
LDUpdate ldupdate = new LDUpdate(dpid, tunnelPortNumber,
UpdateOperation.TUNNEL_PORT_REMOVED);
ldUpdates.add(ldupdate);
}
// *****************
// IOFMessageListener methods
// *****************
@Override
public Command receive(IOFSwitch sw, OFMessage msg,
ListenerContext cntx) {
switch (msg.getType()) {
case FLOW_REMOVED:
// this is meant for tunnel liveness detection but is currently unused
return this.flowRemoved(sw, (OFFlowRemoved) msg, cntx);
case PACKET_IN:
// this takes the sdnplatform Topology Manager path
return this.processPacketInMessage(sw,
(OFPacketIn) msg, cntx);
default:
break;
}
return Command.CONTINUE;
}
}