All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.onosproject.provider.lldpcommon.LinkDiscovery Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2015 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.onosproject.provider.lldpcommon;

import com.google.common.collect.Sets;

import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ONOSLLDP;
import org.onlab.util.Timer;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link.Type;
import org.onosproject.net.LinkKey;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkDescription;
import org.onosproject.net.packet.DefaultOutboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketContext;
import org.slf4j.Logger;

import java.nio.ByteBuffer;
import java.util.Set;

import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.onosproject.net.PortNumber.portNumber;
import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
import static org.slf4j.LoggerFactory.getLogger;
import static org.onosproject.cluster.ClusterMetadata.NO_NAME;

/**
 * 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.
 */
public class LinkDiscovery implements TimerTask {

    private final Logger log = getLogger(getClass());

    private static final String SRC_MAC = "DE:AD:BE:EF:BA:11";

    private final Device device;
    private final LinkDiscoveryContext context;

    private final Ethernet ethPacket;
    private final Ethernet bddpEth;

    private Timeout timeout;
    private volatile boolean isStopped;
    // This LinkDiscovery can handle remote link probes (default false).
    private volatile boolean fingerprinted;
    // Set of ports to be probed
    private final Set ports = Sets.newConcurrentHashSet();

    /**
     * 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 device  the physical switch
     * @param context discovery context
     */
    public LinkDiscovery(Device device, LinkDiscoveryContext context) {
        this.device = device;
        this.context = context;

        ethPacket = new Ethernet();
        ethPacket.setEtherType(Ethernet.TYPE_LLDP);
        ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
        ethPacket.setPad(true);

        bddpEth = new Ethernet();
        bddpEth.setEtherType(Ethernet.TYPE_BSN);
        bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
        bddpEth.setPad(true);

        fingerprinted = false;
        isStopped = true;
        start();
        log.debug("Started discovery manager for switch {}", device.id());

    }

    public synchronized void stop() {
        if (!isStopped) {
            isStopped = true;
            timeout.cancel();
        } else {
            log.warn("LinkDiscovery stopped multiple times?");
        }
    }

    public synchronized void start() {
        if (isStopped) {
            isStopped = false;
            timeout = Timer.getTimer().newTimeout(this, 0, MILLISECONDS);
        } else {
            log.warn("LinkDiscovery started multiple times?");
        }
    }

    public synchronized boolean isStopped() {
        return isStopped || timeout.isCancelled();
    }

    /**
     * Add physical port to discovery process.
     * Send out initial LLDP and label it as slow port.
     *
     * @param port the port
     */
    public void addPort(Port port) {
        boolean newPort = ports.add(port.number().toLong());
        boolean isMaster = context.mastershipService().isLocalMaster(device.id());
        if (newPort && isMaster) {
            log.debug("Sending initial probe to port {}@{}", port.number().toLong(), device.id());
            sendProbes(port.number().toLong());
        }
    }

    /**
     * removed physical port from discovery process.
     * @param port the port number
     */
    public void removePort(PortNumber port) {
        ports.remove(port.toLong());
    }

    /**
     * Handles an incoming LLDP packet. Creates link in topology and adds the
     * link for staleness tracking.
     *
     * @param packetContext packet context
     * @return true if handled
     */
    public boolean handleLldp(PacketContext packetContext) {
        Ethernet eth = packetContext.inPacket().parsed();
        if (eth == null) {
            return false;
        }

        ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
        if (onoslldp != null) {
            if (notMy(onoslldp)) {
                return true;
            }

            PortNumber srcPort = portNumber(onoslldp.getPort());
            PortNumber dstPort = packetContext.inPacket().receivedFrom().port();
            DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
            DeviceId dstDeviceId = packetContext.inPacket().receivedFrom().deviceId();

            ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
            ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);

            LinkDescription ld = eth.getEtherType() == Ethernet.TYPE_LLDP ?
                    new DefaultLinkDescription(src, dst, Type.DIRECT) :
                    new DefaultLinkDescription(src, dst, Type.INDIRECT);

            try {
                context.providerService().linkDetected(ld);
                context.touchLink(LinkKey.linkKey(src, dst));
            } catch (IllegalStateException e) {
                return true;
            }
            return true;
        }
        return false;
    }

    // true if *NOT* this cluster's own probe.
    private boolean notMy(ONOSLLDP onoslldp) {
        if (onoslldp.getDomainTLV() == null) {
            // not finger-printed - but we can check the source
            DeviceId src = DeviceId.deviceId(onoslldp.getDeviceString());
            if (context.deviceService().getDevice(src) == null) {
                return true;
            }
            return false;
        }

        String us = context.fingerprint();
        String them = onoslldp.getDomainString();
        // if: Our and/or their MetadataService in poorly state, conservative 'yes'
        if (NO_NAME.equals(us) || NO_NAME.equals(them)) {
            return true;
        } else {
            return !us.equals(them);
        }
    }

    /**
     * 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
     */
    @Override
    public void run(Timeout t) {
        if (isStopped()) {
            return;
        }

        if (context.mastershipService().isLocalMaster(device.id())) {
            log.trace("Sending probes from {}", device.id());
            ports.forEach(this::sendProbes);
        }

        if (!isStopped()) {
            timeout = Timer.getTimer().newTimeout(this, context.probeRate(), MILLISECONDS);
        }
    }

    /**
     * Creates packet_out LLDP for specified output port.
     *
     * @param port the port
     * @return Packet_out message with LLDP data
     */
    private OutboundPacket createOutBoundLldp(Long port) {
        if (port == null) {
            return null;
        }
        ONOSLLDP lldp = getLinkProbe(port);
        ethPacket.setSourceMACAddress(SRC_MAC).setPayload(lldp);
        return new DefaultOutboundPacket(device.id(),
                                         builder().setOutput(portNumber(port)).build(),
                                         ByteBuffer.wrap(ethPacket.serialize()));
    }

    /**
     * Creates packet_out BDDP for specified output port.
     *
     * @param port the port
     * @return Packet_out message with LLDP data
     */
    private OutboundPacket createOutBoundBddp(Long port) {
        if (port == null) {
            return null;
        }
        ONOSLLDP lldp = getLinkProbe(port);
        bddpEth.setSourceMACAddress(SRC_MAC).setPayload(lldp);
        return new DefaultOutboundPacket(device.id(),
                                         builder().setOutput(portNumber(port)).build(),
                                         ByteBuffer.wrap(bddpEth.serialize()));
    }

    private ONOSLLDP getLinkProbe(Long port) {
        return fingerprinted
                ? ONOSLLDP.fingerprintedLLDP(device.id().toString(), device.chassisId(),
                                             port.intValue(), context.fingerprint())
                : ONOSLLDP.onosLLDP(device.id().toString(), device.chassisId(),
                                    port.intValue());
    }

    private void sendProbes(Long portNumber) {
        log.trace("Sending probes out to {}@{}", portNumber, device.id());
        OutboundPacket pkt = createOutBoundLldp(portNumber);
        context.packetService().emit(pkt);
        if (context.useBddp()) {
            OutboundPacket bpkt = createOutBoundBddp(portNumber);
            context.packetService().emit(bpkt);
        }
    }

    public boolean containsPort(long portNumber) {
        return ports.contains(portNumber);
    }

    public void enableFingerprint() {
        fingerprinted = true;
    }

    public void disableFingerprint() {
        fingerprinted = false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy