gt:         match.setField(new MatchField(MatchType.DL_DST, mac.clone()));
context: /*
   * Copyright (C) 2014 SDN Hub
     Copyright (C) 2015 Wiktor Kujawa
  
   Licensed under the GNU GENERAL PUBLIC LICENSE, Version 3.
   You may not use this file except in compliance with this License.
   You may obtain a copy of the License at
  
      http://www.gnu.org/licenses/gpl-3.0.txt
  
   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.
  
   *
   */
  
  package org.opendaylight.controller.tee.internal;
  
  import java.net.InetAddress;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashSet;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.ListIterator;
  import java.util.Map;
  import java.util.Set;
  import java.util.concurrent.ExecutionException;
  import java.util.concurrent.Future;
  
  import org.opendaylight.controller.hosttracker.IfIptoHost;
  import org.opendaylight.controller.hosttracker.IfNewHostNotify;
  import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
  import org.opendaylight.controller.sal.action.Action;
  import org.opendaylight.controller.sal.action.Output;
  import org.opendaylight.controller.sal.core.ConstructionException;
  import org.opendaylight.controller.sal.core.Edge;
  import org.opendaylight.controller.sal.core.Node;
  import org.opendaylight.controller.sal.core.NodeConnector;
  import org.opendaylight.controller.sal.core.Property;
  import org.opendaylight.controller.sal.core.UpdateType;
  import org.opendaylight.controller.sal.flowprogrammer.Flow;
  import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerListener;
  import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
  import org.opendaylight.controller.sal.match.Match;
  import org.opendaylight.controller.sal.match.MatchField;
  import org.opendaylight.controller.sal.match.MatchType;
  import org.opendaylight.controller.sal.packet.BitBufferHelper;
  import org.opendaylight.controller.sal.packet.Ethernet;
  import org.opendaylight.controller.sal.packet.IDataPacketService;
  import org.opendaylight.controller.sal.packet.IListenDataPacket;
  import org.opendaylight.controller.sal.packet.IPv4;
  import org.opendaylight.controller.sal.packet.Packet;
  import org.opendaylight.controller.sal.packet.PacketResult;
  import org.opendaylight.controller.sal.packet.RawPacket;
  import org.opendaylight.controller.sal.packet.TCP;
  import org.opendaylight.controller.sal.packet.UDP;
  import org.opendaylight.controller.sal.reader.FlowOnNode;
  import org.opendaylight.controller.sal.topology.TopoEdgeUpdate;
  import org.opendaylight.controller.sal.utils.EtherTypes;
  import org.opendaylight.controller.sal.utils.NetUtils;
  import org.opendaylight.controller.sal.utils.Status;
  import org.opendaylight.controller.statisticsmanager.IStatisticsManager;
  import org.opendaylight.controller.switchmanager.IInventoryListener;
  import org.opendaylight.controller.switchmanager.ISwitchManager;
  import org.opendaylight.controller.tee.internal.monitoring.ArpTable;
  import org.opendaylight.controller.tee.internal.monitoring.Device;
  import org.opendaylight.controller.tee.internal.monitoring.Link;
  import org.opendaylight.controller.tee.internal.monitoring.LogicalFlow;
  import org.opendaylight.controller.tee.internal.monitoring.NetworkMonitor;
  import org.opendaylight.controller.tee.internal.monitoring.Route;
  import org.opendaylight.controller.tee.internal.monitoring.RoutesMap;
  import org.opendaylight.controller.tee.internal.monitoring.Utils;
  import org.opendaylight.controller.tee.internal.monitoring.shortestpath.Path;
  import org.opendaylight.controller.topologymanager.ITopologyManager;
  import org.opendaylight.controller.topologymanager.ITopologyManagerAware;
  import org.osgi.framework.Bundle;
  import org.osgi.framework.BundleContext;
  import org.osgi.framework.BundleException;
  import org.osgi.framework.FrameworkUtil;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import com.google.common.net.InetAddresses;
  
  public class TEE implements IListenDataPacket,
          ITopologyManagerAware, IfNewHostNotify, IInventoryListener,
          IFlowProgrammerListener,ITEE{
      private static final Logger logger = LoggerFactory
              .getLogger(TEE.class);
      private ISwitchManager switchManager = null;
      private IFlowProgrammerService programmer = null;
      private IDataPacketService dataPacketService = null;
      private ITopologyManager topologyManager = null;
      private IStatisticsManager statisticsManager = null;
      private IfIptoHost hostTracker = null;
      private NetworkMonitor networkMonitor = null;
  
      private int K = 5;
      private RoutesMap routesMap = new RoutesMap();
      private ArpTable arpTable = new ArpTable();
  
      // Match configuration, read from configuration
      private boolean useTpDst = false;
      private boolean useTpSrc = false;
      private boolean useNwProto = false;
      // Flow timeouts, read from configuration
      private static short MOVING_TIMEOUT = 10; // idle timeout for flow to be removed
      private static short FLOW_TIMEOUT = 60;  // timeout for ordinary flow
  
      private static short FLOW_PRIORITY = 100;  // default priority
      private static short HOST_FLOW_PRIORITY = 200;  // host flow priority
  
      // Last used id for Route
      private static int lastRouteId = 0;
      // Last used id for LogicalFLow
      private static int lastFlowId = 0;
  
      void setDataPacketService(IDataPacketService s) {
          this.dataPacketService = s;
      }
  
      void unsetDataPacketService(IDataPacketService s) {
          if (this.dataPacketService == s) {
              this.dataPacketService = null;
          }
      }
  
      void setFlowProgrammerService(IFlowProgrammerService s) {
          this.programmer = s;
      }
  
      void unsetFlowProgrammerService(IFlowProgrammerService s) {
          if (this.programmer == s) {
              this.programmer = null;
          }
      }
  
      void setSwitchManager(ISwitchManager s) {
          logger.debug("SwitchManager set");
          this.switchManager = s;
      }
  
      void unsetSwitchManager(ISwitchManager s) {
          if (this.switchManager == s) {
              logger.debug("SwitchManager removed!");
              this.switchManager = null;
          }
      }
  
      void setTopologyManager(ITopologyManager s) {
          logger.debug("ITopologyManager set");
          this.topologyManager = s;
      }
  
      void unsetTopologyManager(ITopologyManager s) {
          if (this.topologyManager == s) {
              logger.debug("ITopologyManager removed!");
              this.topologyManager = null;
          }
      }
  
      void setStatisticsManager(IStatisticsManager s) {
          logger.debug("IStatisticsManager set");
          this.statisticsManager = s;
      }
  
      void unsetStatisticsManager(IStatisticsManager s) {
          if (this.statisticsManager == s) {
              logger.debug("IStatisticsManager removed!");
              this.statisticsManager = null;
          }
      }
  
      void setHostTracker(IfIptoHost s) {
          logger.debug("IfIptoHost set");
          this.hostTracker = s;
      }
  
      void unsetHostTracker(IfIptoHost s) {
          if (this.hostTracker == s) {
              logger.debug("IfIptoHost removed!");
              this.hostTracker = null;
          }
      }
  
      /**
       * Function called by the dependency manager when all the required
       * dependencies are satisfied
       *
       */
      void init() {
          logger.info("Initialized");
          // Disabling the SimpleForwarding and ARPHandler bundle to not conflict
          // with this one
          BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
                  .getBundleContext();
          for (Bundle bundle : bundleContext.getBundles()) {
              if (bundle.getSymbolicName().contains("simpleforwarding")) {
                  try {
                      bundle.uninstall();
                  } catch (BundleException e) {
                      logger.error(
                              "Exception in Bundle uninstall "
                                      + bundle.getSymbolicName(), e);
                  }
              }
          }
  
          readConfiguration();
  
          logger.info("Starting Network Monitor..");
          networkMonitor = new NetworkMonitor();
      }
  
      /**
       * Function called by the dependency manager when at least one dependency
       * become unsatisfied or when the component is shutting down because for
       * example bundle is being stopped.
       *
       */
      void destroy() {
      }
  
      /**
       * Function called by dependency manager after "init ()" is called and after
       * the services provided by the class are registered in the service registry
       *
       */
      void start() {
          logger.info("Started");
          networkMonitor.setStatisticsManager(statisticsManager);
          networkMonitor.setSwitchManager(switchManager);
          for (Node node : switchManager.getNodes()) {
              networkMonitor.addDevice(node);
          }
          //TODO get notifications for added and removed switches
          networkMonitor.addEdges(topologyManager.getEdges());
          for (HostNodeConnector c : hostTracker.getAllHosts()) {
              networkMonitor.addHost(c);
          }
  
          networkMonitor.start();
      }
  
      /**
       * Function called by the dependency manager before the services exported by
       * the component are unregistered, this will be followed by a "destroy ()"
       * calls
       *
       */
      void stop() {
          networkMonitor.stop();
          logger.info("Stopped");
      }
  
      /**
       * Retrieves user configurations from config.ini and updates:
       * <ul>
       *  <li>k value for k-shortest path</li>
       * </ul>
       */
      private void readConfiguration() {
          String kStr = System.getProperty("tee.k", "5");
          String matchFieldsStr = System.getProperty("tee.matchFields");
          String movingTimeoutStr = System.getProperty("tee.movingTimeout");
          String flowTimeoutStr = System.getProperty("tee.flowTimeout");
  
          if (matchFieldsStr != null) {
              matchFieldsStr.toUpperCase();
              if (matchFieldsStr.contains("NW_PROTO")) {
                  useNwProto = true;
              }
              if (matchFieldsStr.contains("TP_SRC")) {
                  useTpSrc = true;
              }
              if (matchFieldsStr.contains("TP_DST")) {
                  useTpDst = true;
              }
          }
  
  
          if (kStr != null) {
              try {
                  K = Integer.parseInt(kStr);
              } catch (Exception e) {
              }
          }
  
          if (movingTimeoutStr != null) {
              try {
                  MOVING_TIMEOUT = Short.parseShort(movingTimeoutStr);
              } catch (Exception e) {
              }
          }
  
          if (flowTimeoutStr != null) {
              try {
                  FLOW_TIMEOUT = Short.parseShort(flowTimeoutStr);
              } catch (Exception e) {
              }
          }
  
          if (FLOW_TIMEOUT == MOVING_TIMEOUT && FLOW_TIMEOUT != 0) {
              throw new RuntimeException("Check config.ini. FlowTimeout and MovingTimeout cannot be tha same.");
          }
  
          if (FLOW_TIMEOUT == 0) {
              throw new RuntimeException("Check config.ini. FlowTimeout cannot be 0, because setting idle timeout later will not work. Why? ");
          }
      }
  
      ////////////////////
      // IListenDataPacket
      ////////////////////
      /**
       * When new packet arrives between host H1 and host H2 k-shortest path are found.
       * Same k-paths are added for H1->H2 and H2->H1.
       * Best route from k-path is chosen and programmed for both directions using match
       * created by @see makeMatch(). For each direction logical flow is created and assigned
       * to best route. Logical flow consist of unique id and match made by makeMatch that will
       * be enough to manage that flow in future.
       */
      @Override
      public synchronized PacketResult receiveDataPacket(RawPacket inPkt) {
          /* TODO
           * So far it is synchronized to avoid multiple programming of same packet.
           * It would be better to synchronized it individually for src,dst host pairs.
           * */
  
          if (inPkt == null) {
              return PacketResult.IGNORED;
          }
  
          Packet packet = this.dataPacketService.decodeDataPacket(inPkt);
          logger.trace("Got packet {}", packet.toString());
  
          if (!(packet instanceof Ethernet)) {
              logger.trace("Ignoring not ethernet packet");
              return PacketResult.IGNORED;
          } else {
              NodeConnector poConnector = null;  // Connector for Packet Out
              Object payload = packet.getPayload();
              byte[] srcMAC = ((Ethernet) packet).getSourceMACAddress();
              byte[] dstMAC = ((Ethernet) packet).getDestinationMACAddress();
  
             if (NetUtils.isBroadcastMACAddr(srcMAC) || NetUtils.isBroadcastMACAddr(dstMAC)) {
                  //logger.info("Broadcast {} -> {}", Utils.mac2str(srcMAC), Utils.mac2str(dstMAC));
                  //floodPacket(inPkt); //FIXME cannot do that with loops in network
                  logger.trace("Ignoring broadcast packet {} -> {}", Utils.mac2str(srcMAC), Utils.mac2str(dstMAC));
                  return PacketResult.IGNORED;
              }
  
              short ethType = ((Ethernet) packet).getEtherType();
              if (ethType == EtherTypes.IPv4.shortValue()) {
                  logger.info("Got packet {} -> {} at {}", Utils.mac2str(srcMAC),
                          Utils.mac2str(dstMAC), inPkt.getIncomingNodeConnector()
                                  .getNodeConnectorIDString());
  
                  HostNodeConnector srcHostConnector = null;
                  HostNodeConnector dstHostConnector = null;
  
                  if (payload instanceof IPv4) {
                      InetAddress srcIP = NetUtils.getInetAddress(((IPv4) payload).getSourceAddress());
                      InetAddress dstIP = NetUtils.getInetAddress(((IPv4) payload).getDestinationAddress());
  
                      // Host discovery, known (and programmed) hosts are cached in arpTable
                      srcHostConnector = arpTable.getNodeConnector(BitBufferHelper.getLong(srcMAC));
                      Future<HostNodeConnector> fsrc = null;
                      if (srcHostConnector == null) {
                          logger.info("Discovering {} ...", srcIP.toString());
                          fsrc = hostTracker.discoverHost(srcIP);
                      }
  
                      dstHostConnector = arpTable.getNodeConnector(BitBufferHelper.getLong(dstMAC));
                      Future<HostNodeConnector> fdst = null;
                      if (dstHostConnector == null) {
                          logger.info("Discovering {} ...", dstIP.toString());
                          fdst = hostTracker.discoverHost(dstIP);
                      }
  
                      if (srcHostConnector == null) {
                          try {
                              srcHostConnector = fsrc.get();
                              // Flow leading to host
                              flowToHost(srcMAC, srcHostConnector.getnodeConnector());
                              arpTable.put(BitBufferHelper.toNumber(srcMAC), srcIP.getHostAddress(), srcHostConnector);
                              logger.info("{} () at {}", srcIP, Utils.mac2str(srcMAC),
                                      srcHostConnector.getnodeconnectorNode().getNodeIDString());
                          } catch (InterruptedException | ExecutionException e) {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                          }
                      }
  
                      if (dstHostConnector == null) {
                          try {
                              dstHostConnector = fdst.get();
                              // Flow leading to host
                              flowToHost(dstMAC, dstHostConnector.getnodeConnector());
                              arpTable.put(BitBufferHelper.toNumber(dstMAC), dstIP.getHostAddress(), dstHostConnector);
                              logger.info("{} () at {}", dstIP, Utils.mac2str(dstMAC),
                                      dstHostConnector.getnodeconnectorNode().getNodeIDString());
                          } catch (InterruptedException | ExecutionException e) {
                              // TODO Auto-generated catch block
                              e.printStackTrace();
                          }
                      }
                  } else {
                      logger.error("Not IPV4. Not programming.");
                      return PacketResult.KEEP_PROCESSING;
                  }
  
                  List<Route> routes = routesMap.getRoutes(srcMAC, dstMAC);
                  if (routes.isEmpty()) {
                      //If both host are connected to same node don't need to find route
                      if (srcHostConnector.getnodeconnectorNode().equals(dstHostConnector.getnodeconnectorNode())) {
                          poConnector = dstHostConnector.getnodeConnector();
                      } else {
                          logger.info("Looking for k-paths");
                          routes = pathsToRoutes(networkMonitor.getKShortestPath(srcHostConnector.getnodeconnectorNode(), dstHostConnector.getnodeconnectorNode(),K));
  
                          logger.info("--- K Shortest Paths ---"); //TODO remove debug logs
                          for (Route route : routes) {
                              logger.info("Path:");
                              for (Device device : route.getPath().getVertices()) {
                                  logger.info("Device: "+device);
                              }
                              logger.info("Cost: {} PDR: {}",route.getCost(),route.getPacketsDropped());
                          }
                          logger.info("------------------------");
  
                          routesMap.addRoutes(routes, srcMAC, dstMAC);
                          routesMap.addRoutes(routes, dstMAC, srcMAC);
                      }
                  } else {
                      Match match = makeMatch((Ethernet) packet, false);
                      boolean found = false;
                      for (Route route : routes) {
                          for (LogicalFlow logicalFlow : route.getFlows()) {
                              if (logicalFlow.equals(match)) {
                                  found = true;
                                  break;
                              }
                          }
                          if (found) {
                              break;
                          }
                      }
  
                      // Route is programmed. Just send packet as Packet-Out.
                      if (found) {
                          Route bestRoute = routesMap.getBestRoute(srcMAC, dstMAC);
                          poConnector = getOutputConnector(bestRoute, srcHostConnector);
                      }
                  }
  
                  // Program routes in both way for the first time
                  if (poConnector == null) { //Need to program route
                      Route bestRoute = routesMap.getBestRoute(srcMAC, dstMAC);
                      programRouteBidirect((Ethernet) packet, bestRoute);
  
                      poConnector = getOutputConnector(bestRoute, srcHostConnector);
                  }
  
                  try {
                      RawPacket rawPacket = new RawPacket(inPkt);
                      rawPacket.setOutgoingNodeConnector(poConnector);
                      logger.info("Sending Packet: {}",
                              rawPacket.getOutgoingNodeConnector());
                      dataPacketService.transmitDataPacket(rawPacket);
                  } catch (ConstructionException e) {
                      logger.error("Cannot construct packet: {}",e);
                  }
  
                  logger.debug("Packet consumed");
                  return PacketResult.CONSUME;
              }
              logger.trace("Packet ignored - not IPv4");
              return PacketResult.IGNORED;
          }
      }
  
      ////////////////////
      // Flows programming
      ////////////////////
      /**
       * Programs flows on route to handle given packet in both ways.
       * Creates logical flows for it.
       *
       * @param ethpacket - match will be made based on that
       * @param route - route to program
       * @return
       */
      private boolean programRouteBidirect(Ethernet ethpacket, Route route) {
          boolean ret1 = programRoute(makeLogicalFlow(ethpacket, false), route);
          boolean ret2 = programRoute(makeLogicalFlow(ethpacket, true), route);
  
          return ret1 && ret2;
      }
  
      private Match makeFlowToHostMatch(byte [] mac) {
          Match match = new Match();
  