/*
* 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.linkdiscovery;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.openflow.protocol.OFPhysicalPort;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.util.HexString;
import org.sdnplatform.core.IInfoProvider;
import org.sdnplatform.core.IOFMessageListener;
import org.sdnplatform.core.IOFSwitch;
import org.sdnplatform.core.IOFSwitchListener;
import org.sdnplatform.core.module.ModuleContext;
import org.sdnplatform.core.module.IModule;
import org.sdnplatform.linkdiscovery.ILinkDiscovery.LDUpdate;
import org.sdnplatform.linkdiscovery.ILinkDiscovery.UpdateOperation;
import org.sdnplatform.linkdiscovery.internal.LinkDiscoveryManager;
import org.sdnplatform.packet.IPv4;
import org.sdnplatform.packet.LLDPTLV;
import org.sdnplatform.routing.Link;
import org.sdnplatform.storage.IStorageSourceListener;
import org.sdnplatform.tunnelmanager.ITunnelManagerService;
import org.sdnplatform.vendor.OFActionMirror;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BetterLinkDiscoveryManager extends LinkDiscoveryManager
implements IOFMessageListener, IOFSwitchListener,
IStorageSourceListener, ILinkDiscoveryService,
IModule, IInfoProvider {
protected static Logger log =
LoggerFactory.getLogger(BetterLinkDiscoveryManager.class);
ITunnelManagerService tunnelManager;
@Override
public void startUp(ModuleContext context) {
super.startUp(context);
}
@Override
public void init (ModuleContext context) {
try {
super.init(context);
tunnelManager = context.getServiceImpl(ITunnelManagerService.class);
} catch (Exception e) {
log.warn("{}", e);
}
}
/**
* This method should ideally be in OFSwitchImpl that should be
* overridden when sub-classing. This way, every switch can
* implement their own versions of identifying a tunnel port
* if they are indeed different
*/
@Override
public boolean isTunnelPort(long sw, short port) {
if (tunnelManager == null) return false;
Short tunnelPort = tunnelManager.getTunnelPortNumber(sw);
if (tunnelPort == null || tunnelPort.shortValue() != port)
return false;
return true;
}
@Override
public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) {
if (lt == null)
return ILinkDiscovery.LinkType.INVALID_LINK;
IOFSwitch srcSw = controllerProvider.getSwitches().get(lt.getSrc());
IOFSwitch dstSw = controllerProvider.getSwitches().get(lt.getDst());
if (srcSw == null || dstSw == null)
return ILinkDiscovery.LinkType.INVALID_LINK;
if (isTunnelPort(lt.getSrc(), lt.getSrcPort()) ||
isTunnelPort(lt.getDst(), lt.getDstPort())) {
return ILinkDiscovery.LinkType.TUNNEL;
}
if (info == null) return ILinkDiscovery.LinkType.INVALID_LINK;
if (info.getUnicastValidTime() != null) {
return ILinkDiscovery.LinkType.DIRECT_LINK;
} else if (info.getMulticastValidTime() != null) {
return ILinkDiscovery.LinkType.MULTIHOP_LINK;
}
return ILinkDiscovery.LinkType.INVALID_LINK;
}
@Override
protected void setControllerTLV() {
//Setting the controllerTLVValue based on current nano time,
//controller's IP address, and the network interface object hash
//the corresponding IP address.
final int prime = 7867;
InetAddress localIPAddress = null;
NetworkInterface localInterface = null;
byte[] controllerTLVValue = new byte[] {0, 0, 0, 0, 0, 0, 0, 0}; // 8 byte value.
ByteBuffer bb = ByteBuffer.allocate(10);
try{
localIPAddress = java.net.InetAddress.getLocalHost();
localInterface = NetworkInterface.getByInetAddress(localIPAddress);
} catch (Exception e) {
e.printStackTrace();
}
long result = System.nanoTime();
if (localIPAddress != null)
result = result * prime + IPv4.toIPv4Address(localIPAddress.getHostAddress());
if (localInterface != null)
result = result * prime + localInterface.hashCode();
// Set the first 4 bits to 0x7, so that SDN platform is higher than sdnplatform.
result = result | (0x7000000000000000L);
log.trace("Controller TLV: {}", result);
bb.putLong(result);
bb.rewind();
bb.get(controllerTLVValue, 0, 8);
this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) 8).setValue(controllerTLVValue);
}
/**
* This method overrides the one in LinkDiscovery to special-case
* tunnel links.
*/
@Override
protected boolean isLinkAllowed(long src, short srcPort,
long dst, short dstPort) {
// tunnel manager doesn't exist.
if (tunnelManager == null) return true;
Short srcTunnelPort = tunnelManager.getTunnelPortNumber(src);
Short dstTunnelPort = tunnelManager.getTunnelPortNumber(dst);
boolean srcTunnelFlag = (srcTunnelPort != null &&
srcTunnelPort.shortValue() == srcPort);
boolean dstTunnelFlag = (dstTunnelPort != null &&
dstTunnelPort.shortValue() == dstPort);
if (srcTunnelFlag && dstTunnelFlag) {
// Tunnel links are not allowed to be handled. However updates are
// being sent for tunnel liveness detection
updates.add(new LDUpdate(src, srcPort, dst, dstPort,
ILinkDiscovery.LinkType.TUNNEL,
UpdateOperation.LINK_UPDATED));
return false;
} else if (srcTunnelFlag || dstTunnelFlag) {
// one of them is tunnel, so there's something wrong
// in the network.
log.warn("Detecting link between a tunnel and a non-tunnel port. {}",
new Link(src, srcPort, dst, dstPort));
return false;
}
return true;
}
/**
* This empty method is to ensure link information is not written to
* the database on the SDN platform -- for scalability; while this
* feature is available on the open-source side. These methods are
* currently in place for scalability. When we work on multi-master,
* we need to sync the links through the database, then we will remove
* and/or re-factor as necessary.
* @param lt
* @param linkInfo
*/
@Override
protected void writeLinkToStorage(Link lt, LinkInfo linkInfo) {
}
/**
* Since we are not writing links to storage, we don't need to remove
* links from storage.
*/
@Override
protected void removeLinkFromStorage(Link lt) {
}
@Override
protected List<OFAction> getDiscoveryActions (IOFSwitch sw, OFPhysicalPort port){
List<OFAction> actions = new ArrayList<OFAction>();
// if overlay bind mode and port mirror is enabled, set the action to mirror
if ((sw.getActions() == 0) && (port.getConfig() == 0x80000000)) {
actions.add(new OFActionMirror(port.getPortNumber()));
} else {
actions.add(new OFActionOutput(port.getPortNumber(), (short) 0));
}
return actions;
}
/**
* Check if outgoing discovery messages are enabled or not.
* @param sw
* @param port
* @param isStandard
* @param isReverse
* @return
*/
@Override
protected boolean isOutgoingDiscoveryAllowed(long sw, short port,
boolean isStandard,
boolean isReverse) {
if (isLinkDiscoverySuppressed(sw, port)) {
/* Dont send LLDPs out of this port as suppressLLDP is set */
return false;
}
IOFSwitch iofSwitch = controllerProvider.getSwitches().get(sw);
if (iofSwitch == null) {
return false;
}
if (port == OFPort.OFPP_LOCAL.getValue()) return false;
OFPhysicalPort ofpPort = iofSwitch.getPort(port);
if (ofpPort == null) {
if (log.isTraceEnabled()) {
log.trace("Null physical port. sw={}, port={}",
HexString.toHexString(sw), port);
}
return false;
}
// if it is a tunnel port, then do not send LLDPs.
if (tunnelManager != null &&
tunnelManager.getTunnelPortNumber(sw) != null &&
tunnelManager.getTunnelPortNumber(sw).shortValue() == port)
return false;
// For fast ports, do not send forward LLDPs or BDDPs.
if (!isReverse && isAutoPortFastFeature() && iofSwitch.isFastPort(port))
return false;
// if overlay bind mode and port mirror is not enabled, do not send LLDPs or BDDPs.
if ((iofSwitch.getActions() == 0) && (ofpPort.getConfig() != 0x80000000)) {
return false;
}
return true;
}
}