OpenDaylight Controller functional modules.
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / DiscoveryService.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.controller.protocol_plugin.openflow.internal;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.Timer;
19 import java.util.TimerTask;
20 import java.util.concurrent.BlockingQueue;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.CopyOnWriteArrayList;
24 import java.util.concurrent.LinkedBlockingQueue;
25
26 import org.eclipse.osgi.framework.console.CommandInterpreter;
27 import org.eclipse.osgi.framework.console.CommandProvider;
28 import org.opendaylight.controller.protocol_plugin.openflow.IDataPacketListen;
29 import org.opendaylight.controller.protocol_plugin.openflow.IDataPacketMux;
30 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
31 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
32 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
33 import org.openflow.protocol.OFPhysicalPort;
34 import org.osgi.framework.BundleContext;
35 import org.osgi.framework.FrameworkUtil;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import org.opendaylight.controller.sal.core.Config;
40 import org.opendaylight.controller.sal.core.ConstructionException;
41 import org.opendaylight.controller.sal.core.Edge;
42 import org.opendaylight.controller.sal.core.ContainerFlow;
43 import org.opendaylight.controller.sal.core.IContainerListener;
44 import org.opendaylight.controller.sal.core.Node;
45 import org.opendaylight.controller.sal.core.NodeConnector;
46 import org.opendaylight.controller.sal.core.Property;
47 import org.opendaylight.controller.sal.core.State;
48 import org.opendaylight.controller.sal.core.UpdateType;
49 import org.opendaylight.controller.sal.discovery.IDiscoveryService;
50 import org.opendaylight.controller.sal.inventory.IPluginInInventoryService;
51 import org.opendaylight.controller.sal.packet.Ethernet;
52 import org.opendaylight.controller.sal.packet.LLDP;
53 import org.opendaylight.controller.sal.packet.LLDPTLV;
54 import org.opendaylight.controller.sal.packet.LinkEncap;
55 import org.opendaylight.controller.sal.packet.PacketResult;
56 import org.opendaylight.controller.sal.packet.RawPacket;
57 import org.opendaylight.controller.sal.utils.EtherTypes;
58 import org.opendaylight.controller.sal.utils.HexEncode;
59 import org.opendaylight.controller.sal.utils.NetUtils;
60 import org.opendaylight.controller.sal.utils.NodeConnectorCreator;
61 import org.opendaylight.controller.sal.utils.NodeCreator;
62
63 /**
64  * The class describes neighbor discovery service for an OpenFlow network.
65  */
66 public class DiscoveryService implements IInventoryShimExternalListener,
67         IDataPacketListen, IContainerListener, CommandProvider {
68     private static Logger logger = LoggerFactory
69             .getLogger(DiscoveryService.class);
70     private IController controller = null;
71     private IDiscoveryService discoveryService = null;
72     private IPluginInInventoryService pluginInInventoryService = null;
73     private IDataPacketMux iDataPacketMux = null;
74
75     private List<NodeConnector> readyListHi = null; // newly added ports go into this list and will be served first
76     private List<NodeConnector> readyListLo = null; // come here after served at least once
77     private List<NodeConnector> waitingList = null; // staging area during quiet period
78     private ConcurrentMap<NodeConnector, Integer> pendingMap = null;// wait for response back
79     private ConcurrentMap<NodeConnector, Edge> edgeMap = null; // openflow edges keyed by head connector
80     private ConcurrentMap<NodeConnector, Integer> agingMap = null; // aging entries keyed by edge port
81     private ConcurrentMap<NodeConnector, Edge> prodMap = null; // production edges keyed by edge port
82
83     private Timer discoveryTimer; // discovery timer
84     private DiscoveryTimerTask discoveryTimerTask; // timer task
85     private long discoveryTimerTick = 1L * 1000; // per tick in msec
86     private int discoveryTimerTickCount = 0; // main tick counter
87     private int discoveryBatchMaxPorts = 500; // max # of ports handled in one batch
88     private int discoveryBatchPauseTicks = 28; // pause a little bit after this point
89     private int discoveryBatchRestartTicks = 30; // periodically restart batching process
90     private int discoveryRetry = 1; // number of retry after initial timeout
91     private int discoveryTimeoutTicks = 2; // timeout 2 sec
92     private int discoveryAgeoutTicks = 120; // age out 2 min
93     private int discoveryConsistencyCheckMultiple = 2; // multiple of discoveryBatchRestartTicks
94     private int discoveryConsistencyCheckTickCount = discoveryBatchPauseTicks; // CC tick counter
95     private int discoveryConsistencyCheckCallingTimes = 0; // # of times CC gets called
96     private int discoveryConsistencyCheckCorrected = 0; // # of cases CC corrected
97     private boolean discoveryConsistencyCheckEnabled = true;// enable or disable CC
98     private boolean discoveryAgingEnabled = true; // enable or disable aging
99     private boolean discoverySpoofingEnabled = true; // enable or disable spoofing neighbor of a production network
100
101     private BlockingQueue<NodeConnector> transmitQ;
102     private Thread transmitThread;
103     private Boolean throttling = false; // if true, no more batching.
104     private volatile Boolean shuttingDown = false;
105
106     private LLDPTLV chassisIdTlv, portIdTlv, ttlTlv, customTlv;
107
108     class DiscoveryTransmit implements Runnable {
109         private final BlockingQueue<NodeConnector> transmitQ;
110
111         DiscoveryTransmit(BlockingQueue<NodeConnector> transmitQ) {
112             this.transmitQ = transmitQ;
113         }
114
115         public void run() {
116             while (true) {
117                 try {
118                     NodeConnector nodeConnector = transmitQ.take();
119                     RawPacket outPkt = createDiscoveryPacket(nodeConnector);
120                     sendDiscoveryPacket(nodeConnector, outPkt);
121                     nodeConnector = null;
122                 } catch (InterruptedException e1) {
123                     logger
124                             .warn("DiscoveryTransmit interupted", e1
125                                     .getMessage());
126                     if (shuttingDown)
127                         return;
128                 } catch (Exception e2) {
129                     e2.printStackTrace();
130                 }
131             }
132         }
133     }
134
135     class DiscoveryTimerTask extends TimerTask {
136         public void run() {
137             checkTimeout();
138             checkAging();
139             doConsistencyCheck();
140             doDiscovery();
141         }
142     }
143
144     private RawPacket createDiscoveryPacket(NodeConnector nodeConnector) {
145         String nodeId = HexEncode.longToHexString((Long) nodeConnector
146                 .getNode().getID());
147
148         // Create LLDP ChassisID TLV
149         byte[] cidValue = LLDPTLV.createChassisIDTLVValue(nodeId);
150         chassisIdTlv.setType((byte) LLDPTLV.TLVType.ChassisID.getValue())
151                 .setLength((short) cidValue.length).setValue(cidValue);
152
153         // Create LLDP PortID TLV
154         String portId = nodeConnector.getNodeConnectorIDString();
155         byte[] pidValue = LLDPTLV.createPortIDTLVValue(portId);
156         portIdTlv.setType((byte) LLDPTLV.TLVType.PortID.getValue())
157                 .setLength((short) pidValue.length).setValue(pidValue);
158
159         // Create LLDP Custom TLV
160         byte[] customValue = LLDPTLV.createCustomTLVValue(nodeConnector.toString());
161         customTlv.setType((byte) LLDPTLV.TLVType.Custom.getValue())
162                 .setLength((short) customValue.length).setValue(customValue);
163
164         // Create LLDP Custom Option list
165         List<LLDPTLV> customList = new ArrayList<LLDPTLV>();
166         customList.add(customTlv);
167
168         // Create discovery pkt
169         LLDP discoveryPkt = new LLDP();
170         discoveryPkt.setChassisId(chassisIdTlv).setPortId(portIdTlv).setTtl(
171                 ttlTlv).setOptionalTLVList(customList);
172
173         RawPacket rawPkt = null;
174         try {
175             // Create ethernet pkt
176                 byte[] sourceMac = getSouceMACFromNodeID(nodeId);
177             Ethernet ethPkt = new Ethernet();
178             ethPkt.setSourceMACAddress(sourceMac).setDestinationMACAddress(
179                     LLDP.LLDPMulticastMac).setEtherType(
180                     EtherTypes.LLDP.shortValue()).setPayload(discoveryPkt);
181
182             byte[] data = ethPkt.serialize();
183             rawPkt = new RawPacket(data);
184             rawPkt.setOutgoingNodeConnector(nodeConnector);
185         } catch (ConstructionException cex) {
186             logger.debug("RawPacket creation caught exception {}", cex
187                     .getMessage());
188         } catch (Exception e) {
189             logger.error("Failed to serialize the LLDP packet: " + e);
190         }
191
192         return rawPkt;
193     }
194
195     private void sendDiscoveryPacket(NodeConnector nodeConnector,
196             RawPacket outPkt) {
197         if (nodeConnector == null) {
198             logger.error("nodeConnector is null");
199             return;
200         }
201
202         if (outPkt == null) {
203             logger.error("outPkt is null");
204             return;
205         }
206
207         long sid = (Long) nodeConnector.getNode().getID();
208         ISwitch sw = controller.getSwitches().get(sid);
209
210         if (sw == null) {
211             logger.error("Switch of swid {} is null", sid);
212             return;
213         }
214
215         if (!sw.isOperational()) {
216             logger.error("Switch {} is not operational", sw);
217             return;
218         }
219
220         if (this.iDataPacketMux == null) {
221             logger.error("Cannot send discover packets out");
222             return;
223         }
224
225         logger.trace("Sending topology discovery pkt thru {}", nodeConnector);
226         this.iDataPacketMux.transmitDataPacket(outPkt);
227     }
228
229     @Override
230     public PacketResult receiveDataPacket(RawPacket inPkt) {
231         if (inPkt == null) {
232             logger.debug("Ignoring null packet");
233             return PacketResult.IGNORED;
234         }
235
236         byte[] data = inPkt.getPacketData();
237         if (data.length <= 0) {
238             logger.trace("Ignoring zero length packet");
239             return PacketResult.IGNORED;
240         }
241
242         if (!inPkt.getEncap().equals(LinkEncap.ETHERNET)) {
243             logger.trace("Ignoring non ethernet packet");
244             return PacketResult.IGNORED;
245         }
246
247         if (((Short) inPkt.getIncomingNodeConnector().getID())
248                 .equals(NodeConnector.SPECIALNODECONNECTORID)) {
249             logger.trace("Ignoring ethernet packet received on special port: "
250                     + inPkt.getIncomingNodeConnector().toString());
251             return PacketResult.IGNORED;
252         }
253
254         Ethernet ethPkt = new Ethernet();
255         try {
256             ethPkt.deserialize(data, 0, data.length * NetUtils.NumBitsInAByte);
257         } catch (Exception e) {
258             logger.warn("Failed to decode LLDP packet from "
259                     + inPkt.getIncomingNodeConnector() + ": " + e);
260             return PacketResult.IGNORED;
261         }
262         if (ethPkt.getPayload() instanceof LLDP) {
263             NodeConnector dst = inPkt.getIncomingNodeConnector();
264             if (!processDiscoveryPacket(dst, ethPkt)) {
265                 /* Spoof the discovery pkt if not generated from us */
266                 spoofDiscoveryPacket(dst, ethPkt);
267             }
268             return PacketResult.CONSUME;
269         }
270         return PacketResult.IGNORED;
271     }
272
273     /*
274      * Spoof incoming discovery frames generated by the production network neighbor switch
275      */
276     private void spoofDiscoveryPacket(NodeConnector dstNodeConnector,
277             Ethernet ethPkt) {
278         if (!this.discoverySpoofingEnabled) {
279             return;
280         }
281
282         if ((dstNodeConnector == null) || (ethPkt == null)) {
283             logger
284                     .trace("Ignoring processing of discovery packet: Null node connector or packet");
285             return;
286         }
287
288         logger.trace("Handle discovery packet {} from {}", ethPkt,
289                 dstNodeConnector);
290
291         LLDP lldp = (LLDP) ethPkt.getPayload();
292
293         try {
294             String nodeId = LLDPTLV.getHexStringValue(lldp.getChassisId().getValue(), lldp.getChassisId().getLength());
295             String portId = LLDPTLV.getStringValue(lldp.getPortId().getValue(), lldp.getPortId().getLength());
296             Node srcNode = new Node(Node.NodeIDType.PRODUCTION, nodeId);
297             NodeConnector srcNodeConnector = NodeConnectorCreator
298                     .createNodeConnector(
299                             NodeConnector.NodeConnectorIDType.PRODUCTION,
300                             portId, srcNode);
301
302             // push it out to Topology
303             Edge edge = null;
304             Set<Property> props = null;
305             try {
306                 edge = new Edge(srcNodeConnector, dstNodeConnector);
307                 props = getProps(dstNodeConnector);
308             } catch (ConstructionException e) {
309                 logger.error(e.getMessage());
310             }
311             addEdge(edge, props);
312
313             logger.trace("Received discovery packet for Edge {}", edge);
314         } catch (Exception e) {
315             e.printStackTrace();
316         }
317     }
318
319     /*
320      * Handle discovery frames generated by our controller
321      * @return true if it's a success
322      */
323     private boolean processDiscoveryPacket(NodeConnector dstNodeConnector,
324             Ethernet ethPkt) {
325         if ((dstNodeConnector == null) || (ethPkt == null)) {
326             logger
327                     .trace("Ignoring processing of discovery packet: Null node connector or packet");
328             return false;
329         }
330
331         logger.trace("Handle discovery packet {} from {}", ethPkt,
332                 dstNodeConnector);
333
334         LLDP lldp = (LLDP) ethPkt.getPayload();
335
336         List<LLDPTLV> optionalTLVList = lldp.getOptionalTLVList();
337         if (optionalTLVList == null) {
338             logger.info("The discovery packet with null custom option from {}",
339                     dstNodeConnector);
340             return false;
341         }
342
343         Node srcNode = null;
344         NodeConnector srcNodeConnector = null;
345         for (LLDPTLV lldptlv : lldp.getOptionalTLVList()) {
346             if (lldptlv.getType() == LLDPTLV.TLVType.Custom.getValue()) {
347                 String ncString = LLDPTLV.getCustomString(lldptlv.getValue(), lldptlv.getLength());
348                 srcNodeConnector = NodeConnector.fromString(ncString);
349                 if (srcNodeConnector != null) {
350                         srcNode = srcNodeConnector.getNode();
351                         /* Check if it's expected */
352                         if (isTracked(srcNodeConnector)) {
353                                 break;
354                     } else {
355                         srcNode = null;
356                         srcNodeConnector = null;
357                     }
358                 }
359             }
360         }
361
362         if ((srcNode == null) || (srcNodeConnector == null)) {
363             logger
364                     .trace(
365                             "Received non-controller generated discovery packet from {}",
366                             dstNodeConnector);
367             return false;
368         }
369
370         // push it out to Topology
371         Edge edge = null;
372         Set<Property> props = null;
373         try {
374             edge = new Edge(srcNodeConnector, dstNodeConnector);
375             props = getProps(dstNodeConnector);
376         } catch (ConstructionException e) {
377             logger.error(e.getMessage());
378         }
379         addEdge(edge, props);
380
381         logger.trace("Received discovery packet for Edge {}", edge);
382
383         return true;
384     }
385
386     public Map<String, Property> getPropMap(NodeConnector nodeConnector) {
387         if (nodeConnector == null) {
388             return null;
389         }
390
391         if (pluginInInventoryService == null) {
392             return null;
393         }
394
395         Map<NodeConnector, Map<String, Property>> props = pluginInInventoryService
396                 .getNodeConnectorProps(false);
397         if (props == null) {
398             return null;
399         }
400
401         return props.get(nodeConnector);
402     }
403
404     public Property getProp(NodeConnector nodeConnector, String propName) {
405         Map<String, Property> propMap = getPropMap(nodeConnector);
406         if (propMap == null) {
407             return null;
408         }
409
410         Property prop = (Property) propMap.get(propName);
411         return prop;
412     }
413
414     public Set<Property> getProps(NodeConnector nodeConnector) {
415         Map<String, Property> propMap = getPropMap(nodeConnector);
416         if (propMap == null) {
417             return null;
418         }
419
420         Set<Property> props = new HashSet<Property>(propMap.values());
421         return props;
422     }
423
424     private boolean isEnabled(NodeConnector nodeConnector) {
425         if (nodeConnector == null) {
426             return false;
427         }
428
429         Config config = (Config) getProp(nodeConnector, Config.ConfigPropName);
430         State state = (State) getProp(nodeConnector, State.StatePropName);
431         return ((config != null) && (config.getValue() == Config.ADMIN_UP)
432                 && (state != null) && (state.getValue() == State.EDGE_UP));
433     }
434
435     private boolean isTracked(NodeConnector nodeConnector) {
436         if (readyListHi.contains(nodeConnector)) {
437             return true;
438         }
439
440         if (readyListLo.contains(nodeConnector)) {
441             return true;
442         }
443
444         if (pendingMap.keySet().contains(nodeConnector)) {
445             return true;
446         }
447
448         if (waitingList.contains(nodeConnector)) {
449             return true;
450         }
451
452         return false;
453     }
454
455     private Set<NodeConnector> getWorkingSet() {
456         Set<NodeConnector> workingSet = new HashSet<NodeConnector>();
457         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
458
459         for (NodeConnector nodeConnector : readyListHi) {
460             if (isOverLimit(workingSet.size())) {
461                 break;
462             }
463
464             workingSet.add(nodeConnector);
465             removeSet.add(nodeConnector);
466         }
467         readyListHi.removeAll(removeSet);
468
469         removeSet.clear();
470         for (NodeConnector nodeConnector : readyListLo) {
471             if (isOverLimit(workingSet.size())) {
472                 break;
473             }
474
475             workingSet.add(nodeConnector);
476             removeSet.add(nodeConnector);
477         }
478         readyListLo.removeAll(removeSet);
479
480         return workingSet;
481     }
482
483     private Boolean isOverLimit(int size) {
484         return ((size >= discoveryBatchMaxPorts) && !throttling);
485     }
486
487     private void addDiscovery() {
488         Map<Long, ISwitch> switches = controller.getSwitches();
489         Set<Long> sidSet = switches.keySet();
490         if (sidSet == null) {
491             return;
492         }
493         for (Long sid : sidSet) {
494             Node node = NodeCreator.createOFNode(sid);
495             addDiscovery(node);
496         }
497     }
498
499     private void addDiscovery(Node node) {
500         Map<Long, ISwitch> switches = controller.getSwitches();
501         ISwitch sw = switches.get((Long) node.getID());
502         List<OFPhysicalPort> ports = sw.getEnabledPorts();
503         if (ports == null) {
504             return;
505         }
506         for (OFPhysicalPort port : ports) {
507             NodeConnector nodeConnector = NodeConnectorCreator
508                     .createOFNodeConnector(port.getPortNumber(), node);
509             if (!readyListHi.contains(nodeConnector)) {
510                 readyListHi.add(nodeConnector);
511             }
512         }
513     }
514
515     private void addDiscovery(NodeConnector nodeConnector) {
516         if (isTracked(nodeConnector)) {
517             return;
518         }
519
520         readyListHi.add(nodeConnector);
521     }
522
523     private Set<NodeConnector> getRemoveSet(Collection<NodeConnector> c,
524             Node node) {
525         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
526         if (c == null) {
527             return removeSet;
528         }
529         for (NodeConnector nodeConnector : c) {
530             if (node.equals(nodeConnector.getNode())) {
531                 removeSet.add(nodeConnector);
532             }
533         }
534         return removeSet;
535     }
536
537     private void removeDiscovery(Node node) {
538         Set<NodeConnector> removeSet;
539
540         removeSet = getRemoveSet(readyListHi, node);
541         readyListHi.removeAll(removeSet);
542
543         removeSet = getRemoveSet(readyListLo, node);
544         readyListLo.removeAll(removeSet);
545
546         removeSet = getRemoveSet(waitingList, node);
547         waitingList.removeAll(removeSet);
548
549         removeSet = getRemoveSet(pendingMap.keySet(), node);
550         for (NodeConnector nodeConnector : removeSet) {
551             pendingMap.remove(nodeConnector);
552         }
553
554         removeSet = getRemoveSet(edgeMap.keySet(), node);
555         for (NodeConnector nodeConnector : removeSet) {
556             removeEdge(nodeConnector, false);
557         }
558
559         removeSet = getRemoveSet(prodMap.keySet(), node);
560         for (NodeConnector nodeConnector : removeSet) {
561             removeProd(nodeConnector);
562         }
563     }
564
565     private void removeDiscovery(NodeConnector nodeConnector) {
566         readyListHi.remove(nodeConnector);
567         readyListLo.remove(nodeConnector);
568         waitingList.remove(nodeConnector);
569         pendingMap.remove(nodeConnector);
570         removeEdge(nodeConnector, false);
571         removeProd(nodeConnector);
572     }
573
574     private void checkTimeout() {
575         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
576         Set<NodeConnector> retrySet = new HashSet<NodeConnector>();
577         int sentCount;
578
579         Set<NodeConnector> pendingSet = pendingMap.keySet();
580         if (pendingSet != null) {
581             for (NodeConnector nodeConnector : pendingSet) {
582                 sentCount = pendingMap.get(nodeConnector);
583                 pendingMap.put(nodeConnector, ++sentCount);
584                 if (sentCount > getDiscoveryFinalTimeoutInterval()) {
585                     // timeout the edge
586                     removeSet.add(nodeConnector);
587                     logger.trace("Discovery timeout {}", nodeConnector);
588                 } else if (sentCount % discoveryTimeoutTicks == 0) {
589                     retrySet.add(nodeConnector);
590                 }
591             }
592         }
593
594         for (NodeConnector nodeConnector : removeSet) {
595             removeEdge(nodeConnector);
596         }
597
598         for (NodeConnector nodeConnector : retrySet) {
599             transmitQ.add(nodeConnector);
600         }
601     }
602
603     private void checkAging() {
604         if (!discoveryAgingEnabled) {
605             return;
606         }
607
608         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
609         int sentCount;
610
611         Set<NodeConnector> agingSet = agingMap.keySet();
612         if (agingSet != null) {
613             for (NodeConnector nodeConnector : agingSet) {
614                 sentCount = agingMap.get(nodeConnector);
615                 agingMap.put(nodeConnector, ++sentCount);
616                 if (sentCount > discoveryAgeoutTicks) {
617                     // age out the edge
618                     removeSet.add(nodeConnector);
619                     logger.trace("Discovery age out {}", nodeConnector);
620                 }
621             }
622         }
623
624         for (NodeConnector nodeConnector : removeSet) {
625             removeProd(nodeConnector);
626         }
627     }
628
629     private void doDiscovery() {
630         if (++discoveryTimerTickCount <= discoveryBatchPauseTicks) {
631             for (NodeConnector nodeConnector : getWorkingSet()) {
632                 pendingMap.put(nodeConnector, 0);
633                 transmitQ.add(nodeConnector);
634             }
635         } else if (discoveryTimerTickCount >= discoveryBatchRestartTicks) {
636             discoveryTimerTickCount = 0;
637             for (NodeConnector nodeConnector : waitingList) {
638                 if (!readyListLo.contains(nodeConnector))
639                     readyListLo.add(nodeConnector);
640             }
641             waitingList.removeAll(readyListLo);
642         }
643     }
644
645     private void doConsistencyCheck() {
646         if (!discoveryConsistencyCheckEnabled) {
647             return;
648         }
649
650         if (++discoveryConsistencyCheckTickCount
651                 % getDiscoveryConsistencyCheckInterval() != 0) {
652             return;
653         }
654
655         discoveryConsistencyCheckCallingTimes++;
656
657         Set<NodeConnector> removeSet = new HashSet<NodeConnector>();
658         Set<NodeConnector> ncSet = edgeMap.keySet();
659         if (ncSet == null) {
660             return;
661         }
662         for (NodeConnector nodeConnector : ncSet) {
663             if (!isEnabled(nodeConnector)) {
664                 removeSet.add(nodeConnector);
665                 discoveryConsistencyCheckCorrected++;
666                 logger.debug("ConsistencyChecker: remove disabled {}",
667                         nodeConnector);
668                 continue;
669             }
670
671             if (!isTracked(nodeConnector)) {
672                 waitingList.add(nodeConnector);
673                 discoveryConsistencyCheckCorrected++;
674                 logger.debug("ConsistencyChecker: add back untracked {}",
675                         nodeConnector);
676                 continue;
677             }
678         }
679
680         for (NodeConnector nodeConnector : removeSet) {
681             removeEdge(nodeConnector, false);
682         }
683
684         // remove stale entries
685         removeSet.clear();
686         for (NodeConnector nodeConnector : waitingList) {
687             if (!isEnabled(nodeConnector)) {
688                 removeSet.add(nodeConnector);
689                 discoveryConsistencyCheckCorrected++;
690                 logger.debug("ConsistencyChecker: remove disabled {}",
691                         nodeConnector);
692             }
693         }
694         waitingList.removeAll(removeSet);
695
696         // Get a snapshot of all the existing switches
697         Map<Long, ISwitch> switches = this.controller.getSwitches();
698         for (ISwitch sw : switches.values()) {
699             for (OFPhysicalPort port : sw.getEnabledPorts()) {
700                 Node node = NodeCreator.createOFNode(sw.getId());
701                 NodeConnector nodeConnector = NodeConnectorCreator
702                         .createOFNodeConnector(port.getPortNumber(), node);
703                 if (!isTracked(nodeConnector)) {
704                     waitingList.add(nodeConnector);
705                     discoveryConsistencyCheckCorrected++;
706                     logger.debug("ConsistencyChecker: add back untracked {}",
707                             nodeConnector);
708                 }
709             }
710         }
711     }
712
713     private void addEdge(Edge edge, Set<Property> props) {
714         if (edge == null) {
715             return;
716         }
717
718         NodeConnector src = edge.getTailNodeConnector();
719         if (!src.getType().equals(
720                 NodeConnector.NodeConnectorIDType.PRODUCTION)) {
721             pendingMap.remove(src);
722             if (!waitingList.contains(src)) {
723                 waitingList.add(src);
724             }
725         } else {
726             NodeConnector dst = edge.getHeadNodeConnector();
727             agingMap.put(dst, 0);
728         }
729
730         // notify routeEngine
731         updateEdge(edge, UpdateType.ADDED, props);
732         logger.trace("Add edge {}", edge);
733     }
734
735     /*
736      * Remove Production edge
737      */
738     private void removeProd(NodeConnector nodeConnector) {
739         agingMap.remove(nodeConnector);
740
741         Edge edge = null;
742         Set<NodeConnector> prodKeySet = prodMap.keySet();
743         if ((prodKeySet != null) && (prodKeySet.contains(nodeConnector))) {
744             edge = prodMap.get(nodeConnector);
745             prodMap.remove(nodeConnector);
746         }
747
748         // notify Topology
749         if (this.discoveryService != null) {
750             this.discoveryService.notifyEdge(edge, UpdateType.REMOVED, null);
751         }
752         logger.trace("Remove {}", nodeConnector);
753     }
754
755     /*
756      * Remove OpenFlow edge
757      */
758     private void removeEdge(NodeConnector nodeConnector, boolean stillEnabled) {
759         pendingMap.remove(nodeConnector);
760         readyListLo.remove(nodeConnector);
761         readyListHi.remove(nodeConnector);
762
763         if (stillEnabled) {
764             // keep discovering
765             if (!waitingList.contains(nodeConnector)) {
766                 waitingList.add(nodeConnector);
767             }
768         } else {
769             // stop it
770             waitingList.remove(nodeConnector);
771         }
772
773         Edge edge = null;
774         Set<NodeConnector> edgeKeySet = edgeMap.keySet();
775         if ((edgeKeySet != null) && (edgeKeySet.contains(nodeConnector))) {
776             edge = edgeMap.get(nodeConnector);
777             edgeMap.remove(nodeConnector);
778         }
779
780         // notify Topology
781         if (this.discoveryService != null) {
782             this.discoveryService.notifyEdge(edge, UpdateType.REMOVED, null);
783         }
784         logger.trace("Remove {}", nodeConnector);
785     }
786
787     private void removeEdge(NodeConnector nodeConnector) {
788         removeEdge(nodeConnector, isEnabled(nodeConnector));
789     }
790
791     private void updateEdge(Edge edge, UpdateType type, Set<Property> props) {
792         if (discoveryService == null) {
793             return;
794         }
795
796         this.discoveryService.notifyEdge(edge, type, props);
797
798         NodeConnector src = edge.getTailNodeConnector(), dst = edge
799                 .getHeadNodeConnector();
800         if (!src.getType().equals(
801                 NodeConnector.NodeConnectorIDType.PRODUCTION)) {
802             if (type == UpdateType.ADDED) {
803                 edgeMap.put(src, edge);
804             } else {
805                 edgeMap.remove(src);
806             }
807         } else {
808             /*
809              * Save Production edge into different DB keyed by the Edge port
810              */
811             if (type == UpdateType.ADDED) {
812                 prodMap.put(dst, edge);
813             } else {
814                 prodMap.remove(dst);
815             }
816         }
817     }
818
819     private void moreToReadyListHi(NodeConnector nodeConnector) {
820         if (readyListLo.contains(nodeConnector)) {
821             readyListLo.remove(nodeConnector);
822             readyListHi.add(nodeConnector);
823         } else if (waitingList.contains(nodeConnector)) {
824             waitingList.remove(nodeConnector);
825             readyListHi.add(nodeConnector);
826         }
827     }
828
829     private void registerWithOSGIConsole() {
830         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
831                 .getBundleContext();
832         bundleContext.registerService(CommandProvider.class.getName(), this,
833                 null);
834     }
835
836     private int getDiscoveryConsistencyCheckInterval() {
837         return discoveryConsistencyCheckMultiple * discoveryBatchRestartTicks;
838     }
839
840     private int getDiscoveryFinalTimeoutInterval() {
841         return (discoveryRetry + 1) * discoveryTimeoutTicks;
842     }
843
844     @Override
845     public String getHelp() {
846         StringBuffer help = new StringBuffer();
847         help.append("---Topology Discovery---\n");
848         help.append("\t prlh                   - Print readyListHi entries\n");
849         help.append("\t prll                   - Print readyListLo entries\n");
850         help.append("\t pwl                    - Print waitingList entries\n");
851         help.append("\t ppl                    - Print pendingList entries\n");
852         help.append("\t ptick                  - Print tick time in msec\n");
853         help.append("\t pcc                    - Print CC info\n");
854         help
855                 .append("\t psize                  - Print sizes of all the lists\n");
856         help.append("\t ptm                    - Print timeout info\n");
857         help.append("\t ecc                        - Enable CC\n");
858         help.append("\t dcc                        - Disable CC\n");
859         help
860                 .append("\t scc [multiple]         - Set/show CC multiple and interval\n");
861         help.append("\t sports [ports]             - Set/show max ports per batch\n");
862         help.append("\t spause [ticks]         - Set/show pause ticks\n");
863         help
864                 .append("\t sdi [ticks]            - Set/show discovery interval in ticks\n");
865         help.append("\t stm [ticks]            - Set/show per timeout ticks\n");
866         help.append("\t sretry [count]             - Set/show num of retries\n");
867         help.append("\t addsw <swid>               - Add a switch\n");
868         help.append("\t remsw <swid>               - Remove a switch\n");
869         help.append("\t page                   - Print aging info\n");
870         help.append("\t sage                   - Set/Show aging time limit\n");
871         help.append("\t eage                       - Enable aging\n");
872         help.append("\t dage                       - Disable aging\n");
873         help.append("\t pthrot                 - Print throttling\n");
874         help.append("\t ethrot                     - Enable throttling\n");
875         help.append("\t dthrot                 - Disable throttling\n");
876         return help.toString();
877     }
878
879     public void _prlh(CommandInterpreter ci) {
880         ci.println("ReadyListHi\n");
881         for (NodeConnector nodeConnector : readyListHi) {
882             if (nodeConnector == null) {
883                 continue;
884             }
885             ci.println(nodeConnector);
886         }
887     }
888
889     public void _prll(CommandInterpreter ci) {
890         ci.println("ReadyListLo\n");
891         for (NodeConnector nodeConnector : readyListLo) {
892             if (nodeConnector == null) {
893                 continue;
894             }
895             ci.println(nodeConnector);
896         }
897     }
898
899     public void _pwl(CommandInterpreter ci) {
900         ci.println("WaitingList\n");
901         for (NodeConnector nodeConnector : waitingList) {
902             if (nodeConnector == null) {
903                 continue;
904             }
905             ci.println(nodeConnector);
906         }
907     }
908
909     public void _ppl(CommandInterpreter ci) {
910         ci.println("PendingList\n");
911         for (NodeConnector nodeConnector : pendingMap.keySet()) {
912             if (nodeConnector == null) {
913                 continue;
914             }
915             ci.println(nodeConnector);
916         }
917     }
918
919     public void _ptick(CommandInterpreter ci) {
920         ci.println("Current timer is " + discoveryTimerTick + " msec per tick");
921     }
922
923     public void _pcc(CommandInterpreter ci) {
924         if (discoveryConsistencyCheckEnabled) {
925             ci.println("ConsistencyChecker is currently enabled");
926         } else {
927             ci.println("ConsistencyChecker is currently disabled");
928         }
929         ci.println("Interval " + getDiscoveryConsistencyCheckInterval());
930         ci.println("Multiple " + discoveryConsistencyCheckMultiple);
931         ci.println("Number of times called "
932                 + discoveryConsistencyCheckCallingTimes);
933         ci.println("Corrected count " + discoveryConsistencyCheckCorrected);
934     }
935
936     public void _ptm(CommandInterpreter ci) {
937         ci.println("Final timeout ticks " + getDiscoveryFinalTimeoutInterval());
938         ci.println("Per timeout ticks " + discoveryTimeoutTicks);
939         ci.println("Retry after initial timeout " + discoveryRetry);
940     }
941
942     public void _psize(CommandInterpreter ci) {
943         ci.println("readyListLo size " + readyListLo.size() + "\n"
944                 + "readyListHi size " + readyListHi.size() + "\n"
945                 + "waitingList size " + waitingList.size() + "\n"
946                 + "pendingMap size " + pendingMap.size() + "\n"
947                 + "edgeMap size " + edgeMap.size() + "\n" + "prodMap size "
948                 + prodMap.size() + "\n" + "agingMap size " + agingMap.size());
949     }
950
951     public void _page(CommandInterpreter ci) {
952         if (this.discoveryAgingEnabled) {
953             ci.println("Aging is enabled");
954         } else {
955             ci.println("Aging is disabled");
956         }
957         ci.println("Current aging time limit " + this.discoveryAgeoutTicks);
958         ci.println("\n");
959         ci
960                 .println("                                Edge                                                       Aging ");
961         Collection<Edge> prodSet = prodMap.values();
962         if (prodSet == null) {
963             return;
964         }
965         for (Edge edge : prodSet) {
966             Integer aging = agingMap.get(edge.getHeadNodeConnector());
967             if (aging != null) {
968                 ci.println(edge + "      " + aging);
969             }
970         }
971         ci.println("\n");
972         ci.println("              NodeConnector                                                                         Edge ");
973         Set<NodeConnector> keySet = prodMap.keySet();
974         if (keySet == null) {
975             return;
976         }
977         for (NodeConnector nc : keySet) {
978             ci.println(nc + "      " + prodMap.get(nc));
979         }
980         return;
981     }
982
983     public void _sage(CommandInterpreter ci) {
984         String val = ci.nextArgument();
985         if (val == null) {
986             ci.println("Please enter aging time limit. Current value "
987                     + this.discoveryAgeoutTicks);
988             return;
989         }
990         try {
991             this.discoveryAgeoutTicks = Integer.parseInt(val);
992         } catch (Exception e) {
993             ci.println("Please enter a valid number");
994         }
995         return;
996     }
997
998     public void _eage(CommandInterpreter ci) {
999         this.discoveryAgingEnabled = true;
1000         ci.println("Aging is enabled");
1001         return;
1002     }
1003
1004     public void _dage(CommandInterpreter ci) {
1005         this.discoveryAgingEnabled = false;
1006         ci.println("Aging is disabled");
1007         return;
1008     }
1009
1010     public void _scc(CommandInterpreter ci) {
1011         String val = ci.nextArgument();
1012         if (val == null) {
1013             ci.println("Please enter CC multiple. Current multiple "
1014                     + discoveryConsistencyCheckMultiple + " (interval "
1015                     + getDiscoveryConsistencyCheckInterval()
1016                     + ") calling times "
1017                     + discoveryConsistencyCheckCallingTimes);
1018             return;
1019         }
1020         try {
1021             discoveryConsistencyCheckMultiple = Integer.parseInt(val);
1022         } catch (Exception e) {
1023             ci.println("Please enter a valid number");
1024         }
1025         return;
1026     }
1027
1028     public void _ecc(CommandInterpreter ci) {
1029         this.discoveryConsistencyCheckEnabled = true;
1030         ci.println("ConsistencyChecker is enabled");
1031         return;
1032     }
1033
1034     public void _dcc(CommandInterpreter ci) {
1035         this.discoveryConsistencyCheckEnabled = false;
1036         ci.println("ConsistencyChecker is disabled");
1037         return;
1038     }
1039
1040     public void _pspf(CommandInterpreter ci) {
1041         if (this.discoverySpoofingEnabled) {
1042             ci.println("Discovery spoofing is enabled");
1043         } else {
1044             ci.println("Discovery spoofing is disabled");
1045         }
1046         return;
1047     }
1048
1049     public void _espf(CommandInterpreter ci) {
1050         this.discoverySpoofingEnabled = true;
1051         ci.println("Discovery spoofing is enabled");
1052         return;
1053     }
1054
1055     public void _dspf(CommandInterpreter ci) {
1056         this.discoverySpoofingEnabled = false;
1057         ci.println("Discovery spoofing is disabled");
1058         return;
1059     }
1060
1061     public void _spause(CommandInterpreter ci) {
1062         String val = ci.nextArgument();
1063         String out = "Please enter pause tick value less than "
1064                 + discoveryBatchRestartTicks + ". Current value is "
1065                 + discoveryBatchPauseTicks;
1066
1067         if (val != null) {
1068             try {
1069                 int pause = Integer.parseInt(val);
1070                 if (pause < discoveryBatchRestartTicks) {
1071                     discoveryBatchPauseTicks = pause;
1072                     return;
1073                 }
1074             } catch (Exception e) {
1075             }
1076         }
1077
1078         ci.println(out);
1079     }
1080
1081     public void _sdi(CommandInterpreter ci) {
1082         String val = ci.nextArgument();
1083         if (val == null) {
1084             ci
1085                     .println("Please enter discovery interval in ticks. Current value is "
1086                             + discoveryBatchRestartTicks);
1087             return;
1088         }
1089         try {
1090             discoveryBatchRestartTicks = Integer.parseInt(val);
1091         } catch (Exception e) {
1092             ci.println("Please enter a valid number");
1093         }
1094         return;
1095     }
1096
1097     public void _sports(CommandInterpreter ci) {
1098         String val = ci.nextArgument();
1099         if (val == null) {
1100             ci.println("Please enter max ports per batch. Current value is "
1101                     + discoveryBatchMaxPorts);
1102             return;
1103         }
1104         try {
1105             discoveryBatchMaxPorts = Integer.parseInt(val);
1106         } catch (Exception e) {
1107             ci.println("Please enter a valid number");
1108         }
1109         return;
1110     }
1111
1112     public void _sretry(CommandInterpreter ci) {
1113         String val = ci.nextArgument();
1114         if (val == null) {
1115             ci.println("Please enter number of retries. Current value is "
1116                     + discoveryRetry);
1117             return;
1118         }
1119         try {
1120             discoveryRetry = Integer.parseInt(val);
1121         } catch (Exception e) {
1122             ci.println("Please enter a valid number");
1123         }
1124         return;
1125     }
1126
1127     public void _stm(CommandInterpreter ci) {
1128         String val = ci.nextArgument();
1129         String out = "Please enter timeout tick value less than "
1130                 + discoveryBatchRestartTicks + ". Current value is "
1131                 + discoveryTimeoutTicks;
1132         if (val != null) {
1133             try {
1134                 int timeout = Integer.parseInt(val);
1135                 if (timeout < discoveryBatchRestartTicks) {
1136                     discoveryTimeoutTicks = timeout;
1137                     return;
1138                 }
1139             } catch (Exception e) {
1140             }
1141         }
1142
1143         ci.println(out);
1144     }
1145
1146     public void _addsw(CommandInterpreter ci) {
1147         String val = ci.nextArgument();
1148         Long sid;
1149         try {
1150             sid = Long.parseLong(val);
1151             Node node = NodeCreator.createOFNode(sid);
1152             addDiscovery(node);
1153         } catch (Exception e) {
1154             ci.println("Please enter a valid number");
1155         }
1156         return;
1157     }
1158
1159     public void _remsw(CommandInterpreter ci) {
1160         String val = ci.nextArgument();
1161         Long sid;
1162         try {
1163             sid = Long.parseLong(val);
1164             Node node = NodeCreator.createOFNode(sid);
1165             removeDiscovery(node);
1166         } catch (Exception e) {
1167             ci.println("Please enter a valid number");
1168         }
1169         return;
1170     }
1171
1172     public void _pthrot(CommandInterpreter ci) {
1173         if (this.throttling) {
1174             ci.println("Throttling is enabled");
1175         } else {
1176             ci.println("Throttling is disabled");
1177         }
1178     }
1179
1180     public void _ethrot(CommandInterpreter ci) {
1181         this.throttling = true;
1182         ci.println("Throttling is enabled");
1183         return;
1184     }
1185
1186     public void _dthrot(CommandInterpreter ci) {
1187         this.throttling = false;
1188         ci.println("Throttling is disabled");
1189         return;
1190     }
1191
1192     @Override
1193     public void updateNode(Node node, UpdateType type, Set<Property> props) {
1194         switch (type) {
1195         case ADDED:
1196             addNode(node, props);
1197             break;
1198         case REMOVED:
1199             removeNode(node);
1200             break;
1201         default:
1202             break;
1203         }
1204     }
1205
1206     @Override
1207     public void updateNodeConnector(NodeConnector nodeConnector,
1208             UpdateType type, Set<Property> props) {
1209         Config config = null;
1210         State state = null;
1211         boolean enabled = false;
1212
1213         for (Property prop : props) {
1214             if (prop.getName().equals(Config.ConfigPropName)) {
1215                 config = (Config) prop;
1216             } else if (prop.getName().equals(State.StatePropName)) {
1217                 state = (State) prop;
1218             }
1219         }
1220         enabled = ((config != null) && (config.getValue() == Config.ADMIN_UP)
1221                 && (state != null) && (state.getValue() == State.EDGE_UP));
1222
1223         switch (type) {
1224         case ADDED:
1225             if (enabled) {
1226                 addDiscovery(nodeConnector);
1227                 logger.trace("ADDED enabled {}", nodeConnector);
1228             } else {
1229                 logger.trace("ADDED disabled {}", nodeConnector);
1230             }
1231             break;
1232         case CHANGED:
1233             if (enabled) {
1234                 addDiscovery(nodeConnector);
1235                 logger.trace("CHANGED enabled {}", nodeConnector);
1236             } else {
1237                 removeDiscovery(nodeConnector);
1238                 logger.trace("CHANGED disabled {}", nodeConnector);
1239             }
1240             break;
1241         case REMOVED:
1242             removeDiscovery(nodeConnector);
1243             logger.trace("REMOVED enabled {}", nodeConnector);
1244             break;
1245         default:
1246             return;
1247         }
1248     }
1249
1250     public void addNode(Node node, Set<Property> props) {
1251         if (node == null)
1252             return;
1253
1254         addDiscovery(node);
1255     }
1256
1257     public void removeNode(Node node) {
1258         if (node == null)
1259             return;
1260
1261         removeDiscovery(node);
1262     }
1263
1264     public void updateNode(Node node, Set<Property> props) {
1265     }
1266
1267     void setController(IController s) {
1268         this.controller = s;
1269     }
1270
1271     void unsetController(IController s) {
1272         if (this.controller == s) {
1273             this.controller = null;
1274         }
1275     }
1276
1277     public void setPluginInInventoryService(IPluginInInventoryService service) {
1278         this.pluginInInventoryService = service;
1279     }
1280
1281     public void unsetPluginInInventoryService(IPluginInInventoryService service) {
1282         this.pluginInInventoryService = null;
1283     }
1284
1285     public void setIDataPacketMux(IDataPacketMux service) {
1286         this.iDataPacketMux = service;
1287     }
1288
1289     public void unsetIDataPacketMux(IDataPacketMux service) {
1290         if (this.iDataPacketMux == service) {
1291             this.iDataPacketMux = null;
1292         }
1293     }
1294
1295     void setDiscoveryService(IDiscoveryService s) {
1296         this.discoveryService = s;
1297     }
1298
1299     void unsetDiscoveryService(IDiscoveryService s) {
1300         if (this.discoveryService == s) {
1301             this.discoveryService = null;
1302         }
1303     }
1304
1305     private void initDiscoveryPacket() {
1306         // Create LLDP ChassisID TLV
1307         chassisIdTlv = new LLDPTLV();
1308         chassisIdTlv.setType((byte) LLDPTLV.TLVType.ChassisID.getValue());
1309
1310         // Create LLDP PortID TLV
1311         portIdTlv = new LLDPTLV();
1312         portIdTlv.setType((byte) LLDPTLV.TLVType.PortID.getValue());
1313
1314         // Create LLDP TTL TLV
1315         byte[] ttl = new byte[] { (byte) 120 };
1316         ttlTlv = new LLDPTLV();
1317         ttlTlv.setType((byte) LLDPTLV.TLVType.TTL.getValue()).setLength(
1318                 (short) ttl.length).setValue(ttl);
1319
1320         customTlv = new LLDPTLV();
1321     }
1322
1323     /**
1324      * Function called by the dependency manager when all the required
1325      * dependencies are satisfied
1326      *
1327      */
1328     void init() {
1329         logger.trace("Init called");
1330
1331         transmitQ = new LinkedBlockingQueue<NodeConnector>();
1332
1333         readyListHi = new CopyOnWriteArrayList<NodeConnector>();
1334         readyListLo = new CopyOnWriteArrayList<NodeConnector>();
1335         waitingList = new CopyOnWriteArrayList<NodeConnector>();
1336         pendingMap = new ConcurrentHashMap<NodeConnector, Integer>();
1337         edgeMap = new ConcurrentHashMap<NodeConnector, Edge>();
1338         agingMap = new ConcurrentHashMap<NodeConnector, Integer>();
1339         prodMap = new ConcurrentHashMap<NodeConnector, Edge>();
1340
1341         discoveryTimer = new Timer("DiscoveryService");
1342         discoveryTimerTask = new DiscoveryTimerTask();
1343
1344         transmitThread = new Thread(new DiscoveryTransmit(transmitQ));
1345
1346         initDiscoveryPacket();
1347
1348         registerWithOSGIConsole();
1349     }
1350
1351     /**
1352      * Function called by the dependency manager when at least one dependency
1353      * become unsatisfied or when the component is shutting down because for
1354      * example bundle is being stopped.
1355      *
1356      */
1357     void destroy() {
1358         transmitQ = null;
1359         readyListHi = null;
1360         readyListLo = null;
1361         waitingList = null;
1362         pendingMap = null;
1363         edgeMap = null;
1364         agingMap = null;
1365         prodMap = null;
1366         discoveryTimer = null;
1367         discoveryTimerTask = null;
1368         transmitThread = null;
1369     }
1370
1371     /**
1372      * Function called by dependency manager after "init ()" is called and after
1373      * the services provided by the class are registered in the service registry
1374      *
1375      */
1376     void start() {
1377         discoveryTimer.schedule(discoveryTimerTask, discoveryTimerTick,
1378                 discoveryTimerTick);
1379         transmitThread.start();
1380     }
1381
1382     /**
1383      * Function called after registering the
1384      * service in OSGi service registry.
1385      */
1386     void started() {
1387         /* get a snapshot of all the existing switches */
1388         addDiscovery();
1389     }
1390
1391     /**
1392      * Function called by the dependency manager before the services exported by
1393      * the component are unregistered, this will be followed by a "destroy ()"
1394      * calls
1395      *
1396      */
1397     void stop() {
1398         shuttingDown = true;
1399         discoveryTimer.cancel();
1400         transmitThread.interrupt();
1401     }
1402
1403     @Override
1404     public void tagUpdated(String containerName, Node n, short oldTag,
1405             short newTag, UpdateType t) {
1406     }
1407
1408     @Override
1409     public void containerFlowUpdated(String containerName,
1410             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
1411     }
1412
1413     @Override
1414     public void nodeConnectorUpdated(String containerName, NodeConnector p,
1415             UpdateType t) {
1416         switch (t) {
1417         case ADDED:
1418             moreToReadyListHi(p);
1419             break;
1420         default:
1421             break;
1422         }
1423     }
1424
1425     @Override
1426     public void containerModeUpdated(UpdateType t) {
1427         // do nothing
1428     }
1429     
1430     private byte[] getSouceMACFromNodeID(String nodeId) {        
1431         byte[] cid = HexEncode.bytesFromHexString(nodeId);
1432         byte[] sourceMac = new byte[6];
1433         int pos = cid.length - sourceMac.length;
1434
1435         if (pos >= 0) {
1436                 System.arraycopy(cid, pos, sourceMac, 0, sourceMac.length);
1437         }
1438         
1439         return sourceMac;
1440     }
1441 }