Use LLDP for inter-instance link discovery.

Brought in OVXLLDP.java (renamed to OnosLldp) from OVX to encapsulate
LLDP building and verifying logic. There's also some small changes to
LLDP.java and LLDPTLV.java from OVX.

LinkDiscoveryManager.java has been changed to use the new OnosLldp class.
All links are now discovered using LLDPs, the BDDP code has been disabled.

Note: LinkDiscoveryManager still quite messy and contains unneeded
functionality which will be cleaned up in later patches.

OK to merge now (passed nightly tests).

Change-Id: Ifd8773f7531dc0f462c31aff1854a0dcd384c153
diff --git a/src/main/java/net/onrc/onos/core/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/LinkDiscoveryManager.java
index ed57586..33dbf57 100644
--- a/src/main/java/net/onrc/onos/core/linkdiscovery/internal/LinkDiscoveryManager.java
+++ b/src/main/java/net/onrc/onos/core/linkdiscovery/internal/LinkDiscoveryManager.java
@@ -27,6 +27,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -67,7 +68,9 @@
 import net.onrc.onos.core.packet.IPv4;
 import net.onrc.onos.core.packet.LLDP;
 import net.onrc.onos.core.packet.LLDPTLV;
+import net.onrc.onos.core.packet.OnosLldp;
 import net.onrc.onos.core.registry.IControllerRegistryService;
+import net.onrc.onos.core.util.SwitchPort;
 
 import org.openflow.protocol.OFMessage;
 import org.openflow.protocol.OFPacketIn;
@@ -81,13 +84,14 @@
 import org.openflow.protocol.OFType;
 import org.openflow.protocol.action.OFAction;
 import org.openflow.protocol.action.OFActionOutput;
+import org.openflow.protocol.action.OFActionType;
 import org.openflow.util.HexString;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * This class sends out LLDP messages containing the sending switch's datapath
- * id as well as the outgoing port number.  Received LLrescDP messages that
+ * id as well as the outgoing port number.  Received LLDP messages that
  * match a known switch cause a new LinkTuple to be created according to the
  * invariant rules listed below.  This new LinkTuple is also passed to routing
  * if it exists to trigger updates.
@@ -252,7 +256,7 @@
     /**
      * Quarantine task.
      */
-    protected SingletonTask bddpTask;
+    //protected SingletonTask bddpTask;
     protected static final int BDDP_TASK_INTERVAL = 100; // 100 ms.
     protected static final int BDDP_TASK_SIZE = 5;       // # of ports per iteration
 
@@ -352,6 +356,7 @@
     /**
      * Quarantine Ports.
      */
+    /*
     protected class QuarantineWorker implements Runnable {
         @Override
         public void run() {
@@ -365,6 +370,7 @@
             }
         }
     }
+    */
 
     /**
      * Add a switch port to the quarantine queue. Schedule the
@@ -604,89 +610,8 @@
                     sw, port);
         }
 
-        // using "nearest customer bridge" MAC address for broadest possible propagation
-        // through provider and TPMR bridges (see IEEE 802.1AB-2009 and 802.1Q-2011),
-        // in particular the Linux bridge which behaves mostly like a provider bridge
-        byte[] chassisId = new byte[]{4, 0, 0, 0, 0, 0, 0}; // filled in later
-        byte[] portId = new byte[]{2, 0, 0}; // filled in later
-        byte[] ttlValue = new byte[]{0, 0x78};
-        // OpenFlow OUI - 00-26-E1
-        byte[] dpidTLVValue = new byte[]{0x0, 0x26, (byte) 0xe1, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+        OFPacketOut po = createLLDPPacketOut(sw, ofpPort, isReverse);
 
-        byte[] dpidArray = new byte[8];
-        ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray);
-        ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2);
-
-        Long dpid = sw;
-        dpidBB.putLong(dpid);
-        // set the ethernet source mac to last 6 bytes of dpid
-        byte[] hardwareAddress = new byte[6];
-        System.arraycopy(dpidArray, 2, hardwareAddress, 0, 6);
-        ofpPort.setHardwareAddress(hardwareAddress);
-        // set the chassis id's value to last 6 bytes of dpid
-        System.arraycopy(dpidArray, 2, chassisId, 1, 6);
-        // set the optional tlv to the full dpid
-        System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8);
-        LLDPTLV dpidTLV = new LLDPTLV().setType((byte) 127)
-                .setLength((short) dpidTLVValue.length).setValue(dpidTLVValue);
-
-        // set the portId to the outgoing port
-        portBB.putShort(port);
-        if (log.isTraceEnabled()) {
-            log.trace("Sending LLDP out of interface: {}/{}",
-                    HexString.toHexString(sw), port);
-        }
-
-        LLDP lldp = new LLDP();
-        lldp.setChassisId(new LLDPTLV().setType((byte) 1).setLength((short) chassisId.length).setValue(chassisId));
-        lldp.setPortId(new LLDPTLV().setType((byte) 2).setLength((short) portId.length).setValue(portId));
-        lldp.setTtl(new LLDPTLV().setType((byte) 3).setLength((short) ttlValue.length).setValue(ttlValue));
-        lldp.getOptionalTLVList().add(dpidTLV);
-
-        // Add the controller identifier to the TLV value.
-        lldp.getOptionalTLVList().add(controllerTLV);
-        if (isReverse) {
-            lldp.getOptionalTLVList().add(REVERSE_TLV);
-        } else {
-            lldp.getOptionalTLVList().add(FORWARD_TLV);
-        }
-
-        Ethernet ethernet;
-        if (isStandard) {
-            ethernet = new Ethernet()
-                    .setSourceMACAddress(ofpPort.getHardwareAddress())
-                    .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING)
-                    .setEtherType(Ethernet.TYPE_LLDP);
-            ethernet.setPayload(lldp);
-        } else {
-            BSN bsn = new BSN(BSN.BSN_TYPE_BDDP);
-            bsn.setPayload(lldp);
-
-            ethernet = new Ethernet()
-                    .setSourceMACAddress(ofpPort.getHardwareAddress())
-                    .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING)
-                    .setEtherType(Ethernet.TYPE_BSN);
-            ethernet.setPayload(bsn);
-        }
-
-
-        // serialize and wrap in a packet out
-        byte[] data = ethernet.serialize();
-        OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
-        po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
-        po.setInPort(OFPort.OFPP_NONE);
-
-        // set actions
-        List<OFAction> actions = new ArrayList<OFAction>();
-        actions.add(new OFActionOutput(port, (short) 0));
-        po.setActions(actions);
-        po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
-
-        // set data
-        po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
-        po.setPacketData(data);
-
-        // send
         try {
             iofSwitch.write(po, null);
             iofSwitch.flush();
@@ -697,6 +622,51 @@
     }
 
     /**
+     * Creates packet_out LLDP for specified output port.
+     *
+     * @param dpid the dpid of the outgoing switch
+     * @param port the outgoing port
+     * @param isReverse whether this is a reverse LLDP or not
+     * @return Packet_out message with LLDP data
+     */
+    private OFPacketOut createLLDPPacketOut(long dpid,
+            final OFPhysicalPort port, boolean isReverse) {
+        // Set up packets
+        // TODO optimize by not creating new packets each time
+        OnosLldp lldpPacket = new OnosLldp();
+
+        Ethernet ethPacket = new Ethernet();
+        ethPacket.setEtherType(Ethernet.TYPE_LLDP);
+        ethPacket.setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING);
+        ethPacket.setPayload(lldpPacket);
+        ethPacket.setPad(true);
+
+        final OFPacketOut packetOut = (OFPacketOut) floodlightProvider.getOFMessageFactory()
+                .getMessage(OFType.PACKET_OUT);
+        packetOut.setBufferId(OFPacketOut.BUFFER_ID_NONE);
+
+        final List<OFAction> actionsList = new LinkedList<OFAction>();
+        final OFActionOutput out = (OFActionOutput) floodlightProvider.getOFMessageFactory()
+                .getAction(OFActionType.OUTPUT);
+        out.setPort(port.getPortNumber());
+        actionsList.add(out);
+        packetOut.setActions(actionsList);
+        final short alen = (short) OFActionOutput.MINIMUM_LENGTH;
+
+        lldpPacket.setSwitch(dpid);
+        lldpPacket.setPort(port.getPortNumber());
+        lldpPacket.setReverse(isReverse);
+        ethPacket.setSourceMACAddress(port.getHardwareAddress());
+
+        final byte[] lldp = ethPacket.serialize();
+        packetOut.setActionsLength(alen);
+        packetOut.setPacketData(lldp);
+        packetOut
+                .setLength((short) (OFPacketOut.MINIMUM_LENGTH + alen + lldp.length));
+        return packetOut;
+    }
+
+    /**
      * Send LLDPs to all switch-ports.
      */
     protected void discoverOnAllPorts() {
@@ -724,8 +694,8 @@
 
                     // If the switch port is not alreayd in the maintenance
                     // queue, add it.
-                    NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
-                    addToMaintenanceQueue(npt);
+                    //NodePortTuple npt = new NodePortTuple(sw, ofp.getPortNumber());
+                    //addToMaintenanceQueue(npt);
                 }
             }
         }
@@ -812,18 +782,8 @@
             return Command.CONTINUE;
         }
 
-        long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong();
-        long otherId = 0;
-        boolean myLLDP = false;
-        Boolean isReverse = null;
-
-        ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue());
-        portBB.position(1);
-
-        Short remotePort = portBB.getShort();
-        IOFSwitch remoteSwitch = null;
-
         // Verify this LLDP packet matches what we're looking for
+        /*
         for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
             if (lldptlv.getType() == 127 && lldptlv.getLength() == 12 &&
                     lldptlv.getValue()[0] == 0x0 && lldptlv.getValue()[1] == 0x26 &&
@@ -850,66 +810,37 @@
                 }
             }
         }
+        */
 
-        if (!myLLDP) {
-            // This is not the LLDP sent by this controller.
-            // If the LLDP message has multicast bit set, then we need to broadcast
-            // the packet as a regular packet.
-            if (isStandard) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Getting standard LLDP from a different controller and quelching it.");
-                }
-                return Command.STOP;
-            }
-            // XXX ONOS: Don't disregard any BDDP messages from other
-            // controllers because they're used for inter-instance link detection
-
-            /*else if (sw <= remoteSwitch.getId()) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Getting BBDP from a different controller. myId {}: remoteId {}", myId, otherId);
-                    log.trace("and my controller id is smaller than the other, so quelching it. myPort {}: rPort {}",
-                    pi.getInPort(), remotePort);
-                }
-                //XXX ONOS: Fix the BDDP broadcast issue
-                //return Command.CONTINUE;
-                return Command.STOP;
-            }*/
-            /*
-            else if (myId < otherId)  {
-                if (log.isTraceEnabled()) {
-                    log.trace("Getting BDDP packets from a different controller" +
-                            "and letting it go through normal processing chain.");
-                }
-                //XXX ONOS: Fix the BDDP broadcast issue
-                //return Command.CONTINUE;
-                return Command.STOP;
-            }
-            */
-        }
-
-
-        if (remoteSwitch == null) {
-            // Ignore LLDPs not generated by Floodlight, or from a switch that has recently
-            // disconnected, or from a switch connected to another Floodlight instance
-            if (log.isTraceEnabled()) {
-                log.trace("Received LLDP from remote switch not connected to the controller");
-            }
+        byte[] packetData = pi.getPacketData();
+        if (!OnosLldp.isOnosLldp(packetData)) {
+            log.trace("Dropping LLDP that wasn't sent by ONOS");
             return Command.STOP;
         }
 
-        if (!remoteSwitch.portEnabled(remotePort)) {
-            if (log.isTraceEnabled()) {
-                log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
+        SwitchPort switchPort = OnosLldp.extractSwitchPort(packetData);
+        long remoteDpid = switchPort.dpid().value();
+        short remotePort = switchPort.port().value();
+        IOFSwitch remoteSwitch = floodlightProvider.getSwitches().get(switchPort.dpid().value());
+
+
+        OFPhysicalPort physicalPort = null;
+        if (remoteSwitch != null) {
+            physicalPort = remoteSwitch.getPort(remotePort);
+            if (!remoteSwitch.portEnabled(remotePort)) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort);
+                }
+                return Command.STOP;
             }
-            return Command.STOP;
-        }
-        if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
-                remotePort))) {
-            if (log.isTraceEnabled()) {
-                log.trace("Ignoring link with suppressed src port: switch {} port {}",
-                        remoteSwitch, remotePort);
+            if (suppressLinkDiscovery.contains(new NodePortTuple(remoteSwitch.getId(),
+                    remotePort))) {
+                if (log.isTraceEnabled()) {
+                    log.trace("Ignoring link with suppressed src port: switch {} port {}",
+                            remoteSwitch, remotePort);
+                }
+                return Command.STOP;
             }
-            return Command.STOP;
         }
         if (!iofSwitch.portEnabled(pi.getInPort())) {
             if (log.isTraceEnabled()) {
@@ -918,13 +849,12 @@
             return Command.STOP;
         }
 
-        OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort);
         int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0;
         physicalPort = iofSwitch.getPort(pi.getInPort());
         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());
+        Link lt = new Link(remoteDpid, remotePort, iofSwitch.getId(), pi.getInPort());
 
 
         Long lastLldpTime = null;
@@ -949,6 +879,8 @@
         // first seen within a small interval, send probe on the
         // reverse link.
 
+        boolean isReverse = OnosLldp.isReverse(lldp);
+
         newLinkInfo = links.get(lt);
         if (newLinkInfo != null && isStandard && !isReverse) {
             Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
@@ -962,37 +894,6 @@
             }
         }
 
-        // XXX ONOS: Don't do this:
-        //   If the received packet is a BDDP packet, then create a reverse BDDP
-        //   link as well.
-        // We want to preserve our semantic of the instance that controls the
-        // destination switch is the one who adds the link to the database.
-        /*
-        if (!isStandard) {
-            Link reverseLink = new Link(lt.getDst(), lt.getDstPort(),
-                    lt.getSrc(), lt.getSrcPort());
-
-            // srcPortState and dstPort state are reversed.
-            LinkInfo reverseInfo =
-                    new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime,
-                            dstPortState, srcPortState);
-
-            addOrUpdateLink(reverseLink, reverseInfo);
-        }
-        */
-
-        // Remove the node ports from the quarantine and maintenance queues.
-        NodePortTuple nptSrc = new NodePortTuple(lt.getSrc(), lt.getSrcPort());
-        NodePortTuple nptDst = new NodePortTuple(lt.getDst(), lt.getDstPort());
-        removeFromQuarantineQueue(nptSrc);
-        removeFromQuarantineQueue(nptDst);
-
-        // XXX ONOS: Don't remove the port from the maintenance queue here
-        // because it sometimes prevents BDDPs from being sent and causes
-        // links to flap
-        // removeFromMaintenanceQueue(nptSrc);
-        // removeFromMaintenanceQueue(nptDst);
-
         // Consume this message
         return Command.STOP;
     }
@@ -1772,8 +1673,8 @@
 
         // Setup the BDDP task.  It is invoked whenever switch port tuples
         // are added to the quarantine list.
-        bddpTask = new SingletonTask(ses, new QuarantineWorker());
-        bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
+        //bddpTask = new SingletonTask(ses, new QuarantineWorker());
+        //bddpTask.reschedule(BDDP_TASK_INTERVAL, TimeUnit.MILLISECONDS);
 
 
         // Register for the OpenFlow messages we want to receive