diff --git a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
index 6ba995b9c24f72e63a4b4ace7f95beefda2973f4..25765b3632e88f8d2da25f824ae27fb0ca1ccfe8 100644
--- a/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
+++ b/src/main/java/net/floodlightcontroller/forwarding/Forwarding.java
@@ -17,7 +17,6 @@
 
 package net.floodlightcontroller.forwarding;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -89,584 +88,575 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class Forwarding extends ForwardingBase implements IFloodlightModule, IOFSwitchListener, ILinkDiscoveryListener {
-	protected static Logger log = LoggerFactory.getLogger(Forwarding.class);
-
-	@Override
-	public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
-		Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-		// We found a routing decision (i.e. Firewall is enabled... it's the only thing that makes RoutingDecisions)
-		if (decision != null) {
-			if (log.isTraceEnabled()) {
-				log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi);
-			}
-
-			switch(decision.getRoutingAction()) {
-			case NONE:
-				// don't do anything
-				return Command.CONTINUE;
-			case FORWARD_OR_FLOOD:
-			case FORWARD:
-				doForwardFlow(sw, pi, cntx, false);
-				return Command.CONTINUE;
-			case MULTICAST:
-				// treat as broadcast
-				doFlood(sw, pi, cntx);
-				return Command.CONTINUE;
-			case DROP:
-				doDropFlow(sw, pi, decision, cntx);
-				return Command.CONTINUE;
-			default:
-				log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction());
-				return Command.CONTINUE;
-			}
-		} else { // No routing decision was found. Forward to destination or flood if bcast or mcast.
-			if (log.isTraceEnabled()) {
-				log.trace("No decision was made for PacketIn={}, forwarding", pi);
-			}
-
-			if (eth.isBroadcast() || eth.isMulticast()) {
-				doFlood(sw, pi, cntx);
-			} else {
-				doForwardFlow(sw, pi, cntx, false);
-			}
-		}
-
-		return Command.CONTINUE;
-	}
-
-	protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
-		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-		Match m = createMatchFromPacket(sw, inPort, cntx);
-		OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); // this will be a drop-flow; a flow that will not output to any ports
-		List<OFAction> actions = new ArrayList<OFAction>(); // set no action to drop
-		U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-		log.info("Dropping");
-		fmb.setCookie(cookie)
-		.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
-		.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
-		.setBufferId(OFBufferId.NO_BUFFER) 
-		.setMatch(m)
-		.setPriority(FLOWMOD_DEFAULT_PRIORITY);
-
-		FlowModUtils.setActions(fmb, actions, sw);
-		
+    protected static Logger log = LoggerFactory.getLogger(Forwarding.class);
+
+    @Override
+    public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
+        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+        // We found a routing decision (i.e. Firewall is enabled... it's the only thing that makes RoutingDecisions)
+        if (decision != null) {
+            if (log.isTraceEnabled()) {
+                log.trace("Forwarding decision={} was made for PacketIn={}", decision.getRoutingAction().toString(), pi);
+            }
+
+            switch(decision.getRoutingAction()) {
+            case NONE:
+                // don't do anything
+                return Command.CONTINUE;
+            case FORWARD_OR_FLOOD:
+            case FORWARD:
+                doForwardFlow(sw, pi, cntx, false);
+                return Command.CONTINUE;
+            case MULTICAST:
+                // treat as broadcast
+                doFlood(sw, pi, cntx);
+                return Command.CONTINUE;
+            case DROP:
+                doDropFlow(sw, pi, decision, cntx);
+                return Command.CONTINUE;
+            default:
+                log.error("Unexpected decision made for this packet-in={}", pi, decision.getRoutingAction());
+                return Command.CONTINUE;
+            }
+        } else { // No routing decision was found. Forward to destination or flood if bcast or mcast.
+            if (log.isTraceEnabled()) {
+                log.trace("No decision was made for PacketIn={}, forwarding", pi);
+            }
+
+            if (eth.isBroadcast() || eth.isMulticast()) {
+                doFlood(sw, pi, cntx);
+            } else {
+                doForwardFlow(sw, pi, cntx, false);
+            }
+        }
+
+        return Command.CONTINUE;
+    }
+
+    protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
+        OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
+        Match m = createMatchFromPacket(sw, inPort, cntx);
+        OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd(); // this will be a drop-flow; a flow that will not output to any ports
+        List<OFAction> actions = new ArrayList<OFAction>(); // set no action to drop
+        U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
+        log.info("Dropping");
+        fmb.setCookie(cookie)
+        .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+        .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+        .setBufferId(OFBufferId.NO_BUFFER) 
+        .setMatch(m)
+        .setPriority(FLOWMOD_DEFAULT_PRIORITY);
+
+        FlowModUtils.setActions(fmb, actions, sw);
+
         /* Configure for particular switch pipeline */
         fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID);
 
-		try {
-			if (log.isDebugEnabled()) {
-				log.debug("write drop flow-mod sw={} match={} flow-mod={}",
-						new Object[] { sw, m, fmb.build() });
-			}
-			boolean dampened = messageDamper.write(sw, fmb.build());
-			log.debug("OFMessage dampened: {}", dampened);
-		} catch (IOException e) {
-			log.error("Failure writing drop flow mod", e);
-		}
-	}
-
-	protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, boolean requestFlowRemovedNotifn) {
-		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-		IDevice dstDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
-		DatapathId source = sw.getId();
-
-		if (dstDevice != null) {
-			IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);
-
-			if (srcDevice == null) {
-				log.error("No device entry found for source device. Is the device manager running? If so, report bug.");
-				return;
-			}
-
-			if (FLOOD_ALL_ARP_PACKETS && 
-					IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD).getEtherType() 
-					== EthType.ARP) {
-				log.debug("ARP flows disabled in Forwarding. Flooding ARP packet");
-				doFlood(sw, pi, cntx);
-				return;
-			}
-
-			/* Validate that the source and destination are not on the same switch port */
-			boolean on_same_if = false;
-			for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
-				if (sw.getId().equals(dstDap.getNodeId()) && inPort.equals(dstDap.getPortId())) {
-					on_same_if = true;
-				}
-				break;
-			}
-
-			if (on_same_if) {
-				log.info("Both source and destination are on the same switch/port {}/{}. Action = NOP", sw.toString(), inPort);
-				return;
-			}
-
-			SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
-			SwitchPort dstDap = null;
-
-			/* 
-			 * Search for the true attachment point. The true AP is
-			 * not an endpoint of a link. It is a switch port w/o an
-			 * associated link. Note this does not necessarily hold
-			 * true for devices that 'live' between OpenFlow islands.
-			 * 
-			 * TODO Account for the case where a device is actually
-			 * attached between islands (possibly on a non-OF switch
-			 * in between two OpenFlow switches).
-			 */
-			for (SwitchPort ap : dstDaps) {
-				if (topologyService.isEdge(ap.getNodeId(), ap.getPortId())) {
-					dstDap = ap;
-					break;
-				}
-			}	
-
-			/* 
-			 * This should only happen (perhaps) when the controller is
-			 * actively learning a new topology and hasn't discovered
-			 * all links yet, or a switch was in standalone mode and the
-			 * packet in question was captured in flight on the dst point
-			 * of a link.
-			 */
-			if (dstDap == null) {
-				log.warn("Could not locate edge attachment point for device {}. Flooding packet");
-				doFlood(sw, pi, cntx);
-				return; 
-			}
-
-			/* It's possible that we learned packed destination while it was in flight */
-			if (!topologyService.isEdge(source, inPort)) {	
-				log.debug("Packet destination is known, but packet was not received on an edge port (rx on {}/{}). Flooding packet", source, inPort);
-				doFlood(sw, pi, cntx);
-				return; 
-			}				
-
-			Route route = routingEngineService.getRoute(source, 
-					inPort,
-					dstDap.getNodeId(),
-					dstDap.getPortId(), U64.of(0)); //cookie = 0, i.e., default route
-
-			Match m = createMatchFromPacket(sw, inPort, cntx);
-			U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-
-			if (route != null) {
-				if (log.isDebugEnabled()) {
-					log.debug("pushRoute inPort={} route={} " +
-							"destination={}:{}",
-							new Object[] { inPort, route,
-									dstDap.getNodeId(),
-									dstDap.getPortId()});
-				}
-
-
-				log.debug("Cretaing flow rules on the route, match rule: {}", m);
-				pushRoute(route, m, pi, sw.getId(), cookie, 
-						cntx, requestFlowRemovedNotifn,
-						OFFlowModCommand.ADD);	
-			} else {
-				/* Route traverses no links --> src/dst devices on same switch */
-				log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice);
-				Route r = new Route(srcDevice.getAttachmentPoints()[0].getNodeId(), dstDevice.getAttachmentPoints()[0].getNodeId());
-				List<NodePortTuple> path = new ArrayList<NodePortTuple>(2);
-				path.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getNodeId(),
-						srcDevice.getAttachmentPoints()[0].getPortId()));
-				path.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getNodeId(),
-						dstDevice.getAttachmentPoints()[0].getPortId()));
-				r.setPath(path);
-				pushRoute(r, m, pi, sw.getId(), cookie,
-						cntx, requestFlowRemovedNotifn,
-						OFFlowModCommand.ADD);
-			}
-		} else {
-			log.debug("Destination unknown. Flooding packet");
-			doFlood(sw, pi, cntx);
-		}
-	}
-
-	/**
-	 * Instead of using the Firewall's routing decision Match, which might be as general
-	 * as "in_port" and inadvertently Match packets erroneously, construct a more
-	 * specific Match based on the deserialized OFPacketIn's payload, which has been 
-	 * placed in the FloodlightContext already by the Controller.
-	 * 
-	 * @param sw, the switch on which the packet was received
-	 * @param inPort, the ingress switch port on which the packet was received
-	 * @param cntx, the current context which contains the deserialized packet
-	 * @return a composed Match object based on the provided information
-	 */
-	protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) {
-		// The packet in match will only contain the port number.
-		// We need to add in specifics for the hosts we're routing between.
-		Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
-		VlanVid vlan = VlanVid.ofVlan(eth.getVlanID());
-		MacAddress srcMac = eth.getSourceMACAddress();
-		MacAddress dstMac = eth.getDestinationMACAddress();
-
-		Match.Builder mb = sw.getOFFactory().buildMatch();
-		mb.setExact(MatchField.IN_PORT, inPort);
-
-		if (FLOWMOD_DEFAULT_MATCH_MAC) {
-			mb.setExact(MatchField.ETH_SRC, srcMac)
-			.setExact(MatchField.ETH_DST, dstMac);
-		}
-
-		if (FLOWMOD_DEFAULT_MATCH_VLAN) {
-			if (!vlan.equals(VlanVid.ZERO)) {
-				mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan));
-			}
-		}
-
-		// TODO Detect switch type and match to create hardware-implemented flow
-		if (eth.getEtherType() == EthType.IPv4) { /* shallow check for equality is okay for EthType */
-			IPv4 ip = (IPv4) eth.getPayload();
-			IPv4Address srcIp = ip.getSourceAddress();
-			IPv4Address dstIp = ip.getDestinationAddress();
-
-			if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-				mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
-				.setExact(MatchField.IPV4_SRC, srcIp)
-				.setExact(MatchField.IPV4_DST, dstIp);
-			}
-
-			if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
-				/*
-				 * Take care of the ethertype if not included earlier,
-				 * since it's a prerequisite for transport ports.
-				 */
-				if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-					mb.setExact(MatchField.ETH_TYPE, EthType.IPv4);
-				}
-
-				if (ip.getProtocol().equals(IpProtocol.TCP)) {
-					TCP tcp = (TCP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
-					.setExact(MatchField.TCP_SRC, tcp.getSourcePort())
-					.setExact(MatchField.TCP_DST, tcp.getDestinationPort());
-				} else if (ip.getProtocol().equals(IpProtocol.UDP)) {
-					UDP udp = (UDP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
-					.setExact(MatchField.UDP_SRC, udp.getSourcePort())
-					.setExact(MatchField.UDP_DST, udp.getDestinationPort());
-				}
-			}
-		} else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */
-			mb.setExact(MatchField.ETH_TYPE, EthType.ARP);
-		} else if (eth.getEtherType() == EthType.IPv6) {
-			IPv6 ip = (IPv6) eth.getPayload();
-			IPv6Address srcIp = ip.getSourceAddress();
-			IPv6Address dstIp = ip.getDestinationAddress();
-
-			if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-				mb.setExact(MatchField.ETH_TYPE, EthType.IPv6)
-				.setExact(MatchField.IPV6_SRC, srcIp)
-				.setExact(MatchField.IPV6_DST, dstIp);
-			}
-
-			if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
-				/*
-				 * Take care of the ethertype if not included earlier,
-				 * since it's a prerequisite for transport ports.
-				 */
-				if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
-					mb.setExact(MatchField.ETH_TYPE, EthType.IPv6);
-				}
-
-				if (ip.getNextHeader().equals(IpProtocol.TCP)) {
-					TCP tcp = (TCP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
-					.setExact(MatchField.TCP_SRC, tcp.getSourcePort())
-					.setExact(MatchField.TCP_DST, tcp.getDestinationPort());
-				} else if (ip.getNextHeader().equals(IpProtocol.UDP)) {
-					UDP udp = (UDP) ip.getPayload();
-					mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
-					.setExact(MatchField.UDP_SRC, udp.getSourcePort())
-					.setExact(MatchField.UDP_DST, udp.getDestinationPort());
-				}
-			}
-		}
-		return mb.build();
-	}
-
-	/**
-	 * Creates a OFPacketOut with the OFPacketIn data that is flooded on all ports unless
-	 * the port is blocked, in which case the packet will be dropped.
-	 * @param sw The switch that receives the OFPacketIn
-	 * @param pi The OFPacketIn that came to the switch
-	 * @param cntx The FloodlightContext associated with this OFPacketIn
-	 */
-	protected void doFlood(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
-		OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
-		// Set Action to flood
-		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-		List<OFAction> actions = new ArrayList<OFAction>();
-		Set<OFPort> broadcastPorts = this.topologyService.getSwitchBroadcastPorts(sw.getId());
-
-		if (broadcastPorts.isEmpty()) {
-			log.warn("No broadcast ports found. Using FLOOD output action");
-			/* Must be a single-switch w/no links */
-			broadcastPorts = Collections.singleton(OFPort.FLOOD);
-		}
-
-		for (OFPort p : broadcastPorts) {
-			if (p.equals(inPort)) continue;
-			actions.add(sw.getOFFactory().actions().output(p, Integer.MAX_VALUE));
-		}
-		pob.setActions(actions);
-		// log.info("actions {}",actions);
-		// set buffer-id, in-port and packet-data based on packet-in
-		pob.setBufferId(OFBufferId.NO_BUFFER);
-		OFMessageUtils.setInPort(pob, inPort);
-		pob.setData(pi.getData());
-
-		try {
-			if (log.isTraceEnabled()) {
-				log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}",
-						new Object[] {sw, pi, pob.build()});
-			}
-			messageDamper.write(sw, pob.build());
-		} catch (IOException e) {
-			log.error("Failure writing PacketOut switch={} packet-in={} packet-out={}",
-					new Object[] {sw, pi, pob.build()}, e);
-		}
-
-		return;
-	}
-
-	// IFloodlightModule methods
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleServices() {
-		// We don't export any services
-		return null;
-	}
-
-	@Override
-	public Map<Class<? extends IFloodlightService>, IFloodlightService>
-	getServiceImpls() {
-		// We don't have any services
-		return null;
-	}
-
-	@Override
-	public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
-		Collection<Class<? extends IFloodlightService>> l =
-				new ArrayList<Class<? extends IFloodlightService>>();
-		l.add(IFloodlightProviderService.class);
-		l.add(IDeviceService.class);
-		l.add(IRoutingService.class);
-		l.add(ITopologyService.class);
-		l.add(IDebugCounterService.class);
-		l.add(ILinkDiscoveryService.class);
-		return l;
-	}
-
-	@Override
-	public void init(FloodlightModuleContext context) throws FloodlightModuleException {
-		super.init();
-		this.floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
-		this.deviceManagerService = context.getServiceImpl(IDeviceService.class);
-		this.routingEngineService = context.getServiceImpl(IRoutingService.class);
-		this.topologyService = context.getServiceImpl(ITopologyService.class);
-		this.debugCounterService = context.getServiceImpl(IDebugCounterService.class);
-		this.switchService = context.getServiceImpl(IOFSwitchService.class);
-		this.linkService = context.getServiceImpl(ILinkDiscoveryService.class);
-
-		Map<String, String> configParameters = context.getConfigParams(this);
-		String tmp = configParameters.get("hard-timeout");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_HARD_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp);
-			log.info("Default hard timeout set to {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
-		} else {
-			log.info("Default hard timeout not configured. Using {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
-		}
-		tmp = configParameters.get("idle-timeout");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_IDLE_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp);
-			log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
-		} else {
-			log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
-		}
-		tmp = configParameters.get("table-id");
+        if (log.isDebugEnabled()) {
+            log.debug("write drop flow-mod sw={} match={} flow-mod={}",
+                    new Object[] { sw, m, fmb.build() });
+        }
+        boolean dampened = messageDamper.write(sw, fmb.build());
+        log.debug("OFMessage dampened: {}", dampened);
+    }
+
+    protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx, boolean requestFlowRemovedNotifn) {
+        OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
+        IDevice dstDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
+        DatapathId source = sw.getId();
+
+        if (dstDevice != null) {
+            IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);
+
+            if (srcDevice == null) {
+                log.error("No device entry found for source device. Is the device manager running? If so, report bug.");
+                return;
+            }
+
+            if (FLOOD_ALL_ARP_PACKETS && 
+                    IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD).getEtherType() 
+                    == EthType.ARP) {
+                log.debug("ARP flows disabled in Forwarding. Flooding ARP packet");
+                doFlood(sw, pi, cntx);
+                return;
+            }
+
+            /* Validate that the source and destination are not on the same switch port */
+            boolean on_same_if = false;
+            for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
+                if (sw.getId().equals(dstDap.getNodeId()) && inPort.equals(dstDap.getPortId())) {
+                    on_same_if = true;
+                }
+                break;
+            }
+
+            if (on_same_if) {
+                log.info("Both source and destination are on the same switch/port {}/{}. Action = NOP", sw.toString(), inPort);
+                return;
+            }
+
+            SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
+            SwitchPort dstDap = null;
+
+            /* 
+             * Search for the true attachment point. The true AP is
+             * not an endpoint of a link. It is a switch port w/o an
+             * associated link. Note this does not necessarily hold
+             * true for devices that 'live' between OpenFlow islands.
+             * 
+             * TODO Account for the case where a device is actually
+             * attached between islands (possibly on a non-OF switch
+             * in between two OpenFlow switches).
+             */
+            for (SwitchPort ap : dstDaps) {
+                if (topologyService.isEdge(ap.getNodeId(), ap.getPortId())) {
+                    dstDap = ap;
+                    break;
+                }
+            }	
+
+            /* 
+             * This should only happen (perhaps) when the controller is
+             * actively learning a new topology and hasn't discovered
+             * all links yet, or a switch was in standalone mode and the
+             * packet in question was captured in flight on the dst point
+             * of a link.
+             */
+            if (dstDap == null) {
+                log.warn("Could not locate edge attachment point for device {}. Flooding packet");
+                doFlood(sw, pi, cntx);
+                return; 
+            }
+
+            /* It's possible that we learned packed destination while it was in flight */
+            if (!topologyService.isEdge(source, inPort)) {	
+                log.debug("Packet destination is known, but packet was not received on an edge port (rx on {}/{}). Flooding packet", source, inPort);
+                doFlood(sw, pi, cntx);
+                return; 
+            }				
+
+            Route route = routingEngineService.getRoute(source, 
+                    inPort,
+                    dstDap.getNodeId(),
+                    dstDap.getPortId(), U64.of(0)); //cookie = 0, i.e., default route
+
+            Match m = createMatchFromPacket(sw, inPort, cntx);
+            U64 cookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
+
+            if (route != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("pushRoute inPort={} route={} " +
+                            "destination={}:{}",
+                            new Object[] { inPort, route,
+                                    dstDap.getNodeId(),
+                                    dstDap.getPortId()});
+                }
+
+
+                log.debug("Cretaing flow rules on the route, match rule: {}", m);
+                pushRoute(route, m, pi, sw.getId(), cookie, 
+                        cntx, requestFlowRemovedNotifn,
+                        OFFlowModCommand.ADD);	
+            } else {
+                /* Route traverses no links --> src/dst devices on same switch */
+                log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice);
+                Route r = new Route(srcDevice.getAttachmentPoints()[0].getNodeId(), dstDevice.getAttachmentPoints()[0].getNodeId());
+                List<NodePortTuple> path = new ArrayList<NodePortTuple>(2);
+                path.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getNodeId(),
+                        srcDevice.getAttachmentPoints()[0].getPortId()));
+                path.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getNodeId(),
+                        dstDevice.getAttachmentPoints()[0].getPortId()));
+                r.setPath(path);
+                pushRoute(r, m, pi, sw.getId(), cookie,
+                        cntx, requestFlowRemovedNotifn,
+                        OFFlowModCommand.ADD);
+            }
+        } else {
+            log.debug("Destination unknown. Flooding packet");
+            doFlood(sw, pi, cntx);
+        }
+    }
+
+    /**
+     * Instead of using the Firewall's routing decision Match, which might be as general
+     * as "in_port" and inadvertently Match packets erroneously, construct a more
+     * specific Match based on the deserialized OFPacketIn's payload, which has been 
+     * placed in the FloodlightContext already by the Controller.
+     * 
+     * @param sw, the switch on which the packet was received
+     * @param inPort, the ingress switch port on which the packet was received
+     * @param cntx, the current context which contains the deserialized packet
+     * @return a composed Match object based on the provided information
+     */
+    protected Match createMatchFromPacket(IOFSwitch sw, OFPort inPort, FloodlightContext cntx) {
+        // The packet in match will only contain the port number.
+        // We need to add in specifics for the hosts we're routing between.
+        Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
+        VlanVid vlan = VlanVid.ofVlan(eth.getVlanID());
+        MacAddress srcMac = eth.getSourceMACAddress();
+        MacAddress dstMac = eth.getDestinationMACAddress();
+
+        Match.Builder mb = sw.getOFFactory().buildMatch();
+        mb.setExact(MatchField.IN_PORT, inPort);
+
+        if (FLOWMOD_DEFAULT_MATCH_MAC) {
+            mb.setExact(MatchField.ETH_SRC, srcMac)
+            .setExact(MatchField.ETH_DST, dstMac);
+        }
+
+        if (FLOWMOD_DEFAULT_MATCH_VLAN) {
+            if (!vlan.equals(VlanVid.ZERO)) {
+                mb.setExact(MatchField.VLAN_VID, OFVlanVidMatch.ofVlanVid(vlan));
+            }
+        }
+
+        // TODO Detect switch type and match to create hardware-implemented flow
+        if (eth.getEtherType() == EthType.IPv4) { /* shallow check for equality is okay for EthType */
+            IPv4 ip = (IPv4) eth.getPayload();
+            IPv4Address srcIp = ip.getSourceAddress();
+            IPv4Address dstIp = ip.getDestinationAddress();
+
+            if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                mb.setExact(MatchField.ETH_TYPE, EthType.IPv4)
+                .setExact(MatchField.IPV4_SRC, srcIp)
+                .setExact(MatchField.IPV4_DST, dstIp);
+            }
+
+            if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
+                /*
+                 * Take care of the ethertype if not included earlier,
+                 * since it's a prerequisite for transport ports.
+                 */
+                if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                    mb.setExact(MatchField.ETH_TYPE, EthType.IPv4);
+                }
+
+                if (ip.getProtocol().equals(IpProtocol.TCP)) {
+                    TCP tcp = (TCP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+                    .setExact(MatchField.TCP_SRC, tcp.getSourcePort())
+                    .setExact(MatchField.TCP_DST, tcp.getDestinationPort());
+                } else if (ip.getProtocol().equals(IpProtocol.UDP)) {
+                    UDP udp = (UDP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
+                    .setExact(MatchField.UDP_SRC, udp.getSourcePort())
+                    .setExact(MatchField.UDP_DST, udp.getDestinationPort());
+                }
+            }
+        } else if (eth.getEtherType() == EthType.ARP) { /* shallow check for equality is okay for EthType */
+            mb.setExact(MatchField.ETH_TYPE, EthType.ARP);
+        } else if (eth.getEtherType() == EthType.IPv6) {
+            IPv6 ip = (IPv6) eth.getPayload();
+            IPv6Address srcIp = ip.getSourceAddress();
+            IPv6Address dstIp = ip.getDestinationAddress();
+
+            if (FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                mb.setExact(MatchField.ETH_TYPE, EthType.IPv6)
+                .setExact(MatchField.IPV6_SRC, srcIp)
+                .setExact(MatchField.IPV6_DST, dstIp);
+            }
+
+            if (FLOWMOD_DEFAULT_MATCH_TRANSPORT) {
+                /*
+                 * Take care of the ethertype if not included earlier,
+                 * since it's a prerequisite for transport ports.
+                 */
+                if (!FLOWMOD_DEFAULT_MATCH_IP_ADDR) {
+                    mb.setExact(MatchField.ETH_TYPE, EthType.IPv6);
+                }
+
+                if (ip.getNextHeader().equals(IpProtocol.TCP)) {
+                    TCP tcp = (TCP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.TCP)
+                    .setExact(MatchField.TCP_SRC, tcp.getSourcePort())
+                    .setExact(MatchField.TCP_DST, tcp.getDestinationPort());
+                } else if (ip.getNextHeader().equals(IpProtocol.UDP)) {
+                    UDP udp = (UDP) ip.getPayload();
+                    mb.setExact(MatchField.IP_PROTO, IpProtocol.UDP)
+                    .setExact(MatchField.UDP_SRC, udp.getSourcePort())
+                    .setExact(MatchField.UDP_DST, udp.getDestinationPort());
+                }
+            }
+        }
+        return mb.build();
+    }
+
+    /**
+     * Creates a OFPacketOut with the OFPacketIn data that is flooded on all ports unless
+     * the port is blocked, in which case the packet will be dropped.
+     * @param sw The switch that receives the OFPacketIn
+     * @param pi The OFPacketIn that came to the switch
+     * @param cntx The FloodlightContext associated with this OFPacketIn
+     */
+    protected void doFlood(IOFSwitch sw, OFPacketIn pi, FloodlightContext cntx) {
+        OFPort inPort = (pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT));
+        // Set Action to flood
+        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+        List<OFAction> actions = new ArrayList<OFAction>();
+        Set<OFPort> broadcastPorts = this.topologyService.getSwitchBroadcastPorts(sw.getId());
+
+        if (broadcastPorts.isEmpty()) {
+            log.warn("No broadcast ports found. Using FLOOD output action");
+            /* Must be a single-switch w/no links */
+            broadcastPorts = Collections.singleton(OFPort.FLOOD);
+        }
+
+        for (OFPort p : broadcastPorts) {
+            if (p.equals(inPort)) continue;
+            actions.add(sw.getOFFactory().actions().output(p, Integer.MAX_VALUE));
+        }
+        pob.setActions(actions);
+        // log.info("actions {}",actions);
+        // set buffer-id, in-port and packet-data based on packet-in
+        pob.setBufferId(OFBufferId.NO_BUFFER);
+        OFMessageUtils.setInPort(pob, inPort);
+        pob.setData(pi.getData());
+
+        if (log.isTraceEnabled()) {
+            log.trace("Writing flood PacketOut switch={} packet-in={} packet-out={}",
+                    new Object[] {sw, pi, pob.build()});
+        }
+        messageDamper.write(sw, pob.build());
+
+        return;
+    }
+
+    // IFloodlightModule methods
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleServices() {
+        // We don't export any services
+        return null;
+    }
+
+    @Override
+    public Map<Class<? extends IFloodlightService>, IFloodlightService>
+    getServiceImpls() {
+        // We don't have any services
+        return null;
+    }
+
+    @Override
+    public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
+        Collection<Class<? extends IFloodlightService>> l =
+                new ArrayList<Class<? extends IFloodlightService>>();
+        l.add(IFloodlightProviderService.class);
+        l.add(IDeviceService.class);
+        l.add(IRoutingService.class);
+        l.add(ITopologyService.class);
+        l.add(IDebugCounterService.class);
+        l.add(ILinkDiscoveryService.class);
+        return l;
+    }
+
+    @Override
+    public void init(FloodlightModuleContext context) throws FloodlightModuleException {
+        super.init();
+        this.floodlightProviderService = context.getServiceImpl(IFloodlightProviderService.class);
+        this.deviceManagerService = context.getServiceImpl(IDeviceService.class);
+        this.routingEngineService = context.getServiceImpl(IRoutingService.class);
+        this.topologyService = context.getServiceImpl(ITopologyService.class);
+        this.debugCounterService = context.getServiceImpl(IDebugCounterService.class);
+        this.switchService = context.getServiceImpl(IOFSwitchService.class);
+        this.linkService = context.getServiceImpl(ILinkDiscoveryService.class);
+
+        Map<String, String> configParameters = context.getConfigParams(this);
+        String tmp = configParameters.get("hard-timeout");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_HARD_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp);
+            log.info("Default hard timeout set to {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
+        } else {
+            log.info("Default hard timeout not configured. Using {}.", FLOWMOD_DEFAULT_HARD_TIMEOUT);
+        }
+        tmp = configParameters.get("idle-timeout");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_IDLE_TIMEOUT = ParseUtils.parseHexOrDecInt(tmp);
+            log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
+        } else {
+            log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
+        }
+        tmp = configParameters.get("table-id");
         if (tmp != null) {
             FLOWMOD_DEFAULT_TABLE_ID = TableId.of(ParseUtils.parseHexOrDecInt(tmp));
             log.info("Default idle timeout set to {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
         } else {
             log.info("Default idle timeout not configured. Using {}.", FLOWMOD_DEFAULT_IDLE_TIMEOUT);
         }
-		tmp = configParameters.get("priority");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_PRIORITY = ParseUtils.parseHexOrDecInt(tmp);
-			log.info("Default priority set to {}.", FLOWMOD_DEFAULT_PRIORITY);
-		} else {
-			log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY);
-		}
-		tmp = configParameters.get("set-send-flow-rem-flag");
-		if (tmp != null) {
-			FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp);
-			log.info("Default flags will be set to SEND_FLOW_REM {}.", FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG);
-		} else {
-			log.info("Default flags will be empty.");
-		}
-		tmp = configParameters.get("match");
-		if (tmp != null) {
-			tmp = tmp.toLowerCase();
-			if (!tmp.contains("vlan") && !tmp.contains("mac") && !tmp.contains("ip") && !tmp.contains("port")) {
-				/* leave the default configuration -- blank or invalid 'match' value */
-			} else {
-				FLOWMOD_DEFAULT_MATCH_VLAN = tmp.contains("vlan") ? true : false;
-				FLOWMOD_DEFAULT_MATCH_MAC = tmp.contains("mac") ? true : false;
-				FLOWMOD_DEFAULT_MATCH_IP_ADDR = tmp.contains("ip") ? true : false;
-				FLOWMOD_DEFAULT_MATCH_TRANSPORT = tmp.contains("port") ? true : false;
-
-			}
-		}
-		log.info("Default flow matches set to: VLAN=" + FLOWMOD_DEFAULT_MATCH_VLAN
-				+ ", MAC=" + FLOWMOD_DEFAULT_MATCH_MAC
-				+ ", IP=" + FLOWMOD_DEFAULT_MATCH_IP_ADDR
-				+ ", TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT);
-
-		tmp = configParameters.get("flood-arp");
-		if (tmp != null) {
-			tmp = tmp.toLowerCase();
-			if (!tmp.contains("yes") && !tmp.contains("yep") && !tmp.contains("true") && !tmp.contains("ja") && !tmp.contains("stimmt")) {
-				FLOOD_ALL_ARP_PACKETS = false;
-				log.info("Not flooding ARP packets. ARP flows will be inserted for known destinations");
-			} else {
-				FLOOD_ALL_ARP_PACKETS = true;
-				log.info("Flooding all ARP packets. No ARP flows will be inserted");
-			}
-		}
-
-		tmp = configParameters.get("remove-flows-on-link-or-port-down");
-		if (tmp != null) {
-			REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = Boolean.parseBoolean(tmp);
-		}
-		if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
-			log.info("Flows will be removed on link/port down events");
-		} else {
-			log.info("Flows will not be removed on link/port down events");
-		}
-	}
-
-	@Override
-	public void startUp(FloodlightModuleContext context) {
-		super.startUp();
-		switchService.addOFSwitchListener(this);
-
-		/* Register only if we want to remove stale flows */
-		if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
-			linkService.addListener(this);
-		}
-	}
-
-	@Override
-	public void switchAdded(DatapathId switchId) {
-	}
-
-	@Override
-	public void switchRemoved(DatapathId switchId) {		
-	}
-
-	@Override
-	public void switchActivated(DatapathId switchId) {
-		IOFSwitch sw = switchService.getSwitch(switchId);
-		if (sw == null) {
-			log.warn("Switch {} was activated but had no switch object in the switch service. Perhaps it quickly disconnected", switchId);
-			return;
-		}
-		if (OFDPAUtils.isOFDPASwitch(sw)) {
-			sw.write(sw.getOFFactory().buildFlowDelete()
-					.setTableId(TableId.ALL)
-					.build()
-					);
-			sw.write(sw.getOFFactory().buildGroupDelete()
-					.setGroup(OFGroup.ANY)
-					.setGroupType(OFGroupType.ALL)
-					.build()
-					);
-			sw.write(sw.getOFFactory().buildGroupDelete()
-					.setGroup(OFGroup.ANY)
-					.setGroupType(OFGroupType.INDIRECT)
-					.build()
-					);
-			sw.write(sw.getOFFactory().buildBarrierRequest().build());
-
-			List<OFPortModeTuple> portModes = new ArrayList<OFPortModeTuple>();
-			for (OFPortDesc p : sw.getPorts()) {
-				portModes.add(OFPortModeTuple.of(p.getPortNo(), OFPortMode.ACCESS));
-			}
-			if (log.isWarnEnabled()) {
-				log.warn("For OF-DPA switch {}, initializing VLAN {} on ports {}", new Object[] { switchId, VlanVid.ZERO, portModes});
-			}
-			OFDPAUtils.addLearningSwitchPrereqs(sw, VlanVid.ZERO, portModes);
-		}
-	}
-
-	@Override
-	public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) {	
-		/* Port down events handled via linkDiscoveryUpdate(), which passes thru all events */
-	}
-
-	@Override
-	public void switchChanged(DatapathId switchId) {
-	}
-
-	@Override
-	public void switchDeactivated(DatapathId switchId) {
-	}
-
-	@Override
-	public void linkDiscoveryUpdate(List<LDUpdate> updateList) {
-		for (LDUpdate u : updateList) {
-			/* Remove flows on either side if link/port went down */
-			if (u.getOperation() == UpdateOperation.LINK_REMOVED ||
-					u.getOperation() == UpdateOperation.PORT_DOWN ||
-					u.getOperation() == UpdateOperation.TUNNEL_PORT_REMOVED) {
-				Set<OFMessage> msgs = new HashSet<OFMessage>();
-
-				if (u.getSrc() != null && !u.getSrc().equals(DatapathId.NONE)) {
-					IOFSwitch srcSw = switchService.getSwitch(u.getSrc());
-					/* src side of link */
-					if (srcSw != null) {
-						/* flows matching on src port */
-						msgs.add(srcSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setMatch(srcSw.getOFFactory().buildMatch()
-										.setExact(MatchField.IN_PORT, u.getSrcPort())
-										.build())
-								.build());
-						/* flows outputting to src port */
-						msgs.add(srcSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setOutPort(u.getSrcPort())
-								.build());
-						srcSw.write(msgs);
-						log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getSrc(), u.getSrcPort() });
-					}
-				}
-
-				/* must be a link, not just a port down, if we have a dst switch */
-				if (u.getDst() != null && !u.getDst().equals(DatapathId.NONE)) {
-					/* dst side of link */
-					IOFSwitch dstSw = switchService.getSwitch(u.getDst());
-					if (dstSw != null) {
-						/* flows matching on dst port */
-						msgs.clear();
-						msgs.add(dstSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setMatch(dstSw.getOFFactory().buildMatch()
-										.setExact(MatchField.IN_PORT, u.getDstPort())
-										.build())
-								.build());
-						/* flows outputting to dst port */
-						msgs.add(dstSw.getOFFactory().buildFlowDelete()
-								.setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
-								.setOutPort(u.getDstPort())
-								.build());
-						dstSw.write(msgs);
-						log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getDst(), u.getDstPort() });
-					}
-				}
-			}
-		}
-	}
+        tmp = configParameters.get("priority");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_PRIORITY = ParseUtils.parseHexOrDecInt(tmp);
+            log.info("Default priority set to {}.", FLOWMOD_DEFAULT_PRIORITY);
+        } else {
+            log.info("Default priority not configured. Using {}.", FLOWMOD_DEFAULT_PRIORITY);
+        }
+        tmp = configParameters.get("set-send-flow-rem-flag");
+        if (tmp != null) {
+            FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = Boolean.parseBoolean(tmp);
+            log.info("Default flags will be set to SEND_FLOW_REM {}.", FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG);
+        } else {
+            log.info("Default flags will be empty.");
+        }
+        tmp = configParameters.get("match");
+        if (tmp != null) {
+            tmp = tmp.toLowerCase();
+            if (!tmp.contains("vlan") && !tmp.contains("mac") && !tmp.contains("ip") && !tmp.contains("port")) {
+                /* leave the default configuration -- blank or invalid 'match' value */
+            } else {
+                FLOWMOD_DEFAULT_MATCH_VLAN = tmp.contains("vlan") ? true : false;
+                FLOWMOD_DEFAULT_MATCH_MAC = tmp.contains("mac") ? true : false;
+                FLOWMOD_DEFAULT_MATCH_IP_ADDR = tmp.contains("ip") ? true : false;
+                FLOWMOD_DEFAULT_MATCH_TRANSPORT = tmp.contains("port") ? true : false;
+
+            }
+        }
+        log.info("Default flow matches set to: VLAN=" + FLOWMOD_DEFAULT_MATCH_VLAN
+                + ", MAC=" + FLOWMOD_DEFAULT_MATCH_MAC
+                + ", IP=" + FLOWMOD_DEFAULT_MATCH_IP_ADDR
+                + ", TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT);
+
+        tmp = configParameters.get("flood-arp");
+        if (tmp != null) {
+            tmp = tmp.toLowerCase();
+            if (!tmp.contains("yes") && !tmp.contains("yep") && !tmp.contains("true") && !tmp.contains("ja") && !tmp.contains("stimmt")) {
+                FLOOD_ALL_ARP_PACKETS = false;
+                log.info("Not flooding ARP packets. ARP flows will be inserted for known destinations");
+            } else {
+                FLOOD_ALL_ARP_PACKETS = true;
+                log.info("Flooding all ARP packets. No ARP flows will be inserted");
+            }
+        }
+
+        tmp = configParameters.get("remove-flows-on-link-or-port-down");
+        if (tmp != null) {
+            REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = Boolean.parseBoolean(tmp);
+        }
+        if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
+            log.info("Flows will be removed on link/port down events");
+        } else {
+            log.info("Flows will not be removed on link/port down events");
+        }
+    }
+
+    @Override
+    public void startUp(FloodlightModuleContext context) {
+        super.startUp();
+        switchService.addOFSwitchListener(this);
+
+        /* Register only if we want to remove stale flows */
+        if (REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN) {
+            linkService.addListener(this);
+        }
+    }
+
+    @Override
+    public void switchAdded(DatapathId switchId) {
+    }
+
+    @Override
+    public void switchRemoved(DatapathId switchId) {		
+    }
+
+    @Override
+    public void switchActivated(DatapathId switchId) {
+        IOFSwitch sw = switchService.getSwitch(switchId);
+        if (sw == null) {
+            log.warn("Switch {} was activated but had no switch object in the switch service. Perhaps it quickly disconnected", switchId);
+            return;
+        }
+        if (OFDPAUtils.isOFDPASwitch(sw)) {
+            messageDamper.write(sw, sw.getOFFactory().buildFlowDelete()
+                    .setTableId(TableId.ALL)
+                    .build()
+                    );
+            messageDamper.write(sw, sw.getOFFactory().buildGroupDelete()
+                    .setGroup(OFGroup.ANY)
+                    .setGroupType(OFGroupType.ALL)
+                    .build()
+                    );
+            messageDamper.write(sw, sw.getOFFactory().buildGroupDelete()
+                    .setGroup(OFGroup.ANY)
+                    .setGroupType(OFGroupType.INDIRECT)
+                    .build()
+                    );
+            messageDamper.write(sw, sw.getOFFactory().buildBarrierRequest().build());
+
+            List<OFPortModeTuple> portModes = new ArrayList<OFPortModeTuple>();
+            for (OFPortDesc p : sw.getPorts()) {
+                portModes.add(OFPortModeTuple.of(p.getPortNo(), OFPortMode.ACCESS));
+            }
+            if (log.isWarnEnabled()) {
+                log.warn("For OF-DPA switch {}, initializing VLAN {} on ports {}", new Object[] { switchId, VlanVid.ZERO, portModes});
+            }
+            OFDPAUtils.addLearningSwitchPrereqs(sw, VlanVid.ZERO, portModes);
+        }
+    }
+
+    @Override
+    public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) {	
+        /* Port down events handled via linkDiscoveryUpdate(), which passes thru all events */
+    }
+
+    @Override
+    public void switchChanged(DatapathId switchId) {
+    }
+
+    @Override
+    public void switchDeactivated(DatapathId switchId) {
+    }
+
+    @Override
+    public void linkDiscoveryUpdate(List<LDUpdate> updateList) {
+        for (LDUpdate u : updateList) {
+            /* Remove flows on either side if link/port went down */
+            if (u.getOperation() == UpdateOperation.LINK_REMOVED ||
+                    u.getOperation() == UpdateOperation.PORT_DOWN ||
+                    u.getOperation() == UpdateOperation.TUNNEL_PORT_REMOVED) {
+                Set<OFMessage> msgs = new HashSet<OFMessage>();
+
+                if (u.getSrc() != null && !u.getSrc().equals(DatapathId.NONE)) {
+                    IOFSwitch srcSw = switchService.getSwitch(u.getSrc());
+                    /* src side of link */
+                    if (srcSw != null) {
+                        /* flows matching on src port */
+                        msgs.add(srcSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setMatch(srcSw.getOFFactory().buildMatch()
+                                        .setExact(MatchField.IN_PORT, u.getSrcPort())
+                                        .build())
+                                .build());
+                        /* flows outputting to src port */
+                        msgs.add(srcSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setOutPort(u.getSrcPort())
+                                .build());
+                        messageDamper.write(srcSw, msgs);
+                        log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getSrc(), u.getSrcPort() });
+                    }
+                }
+
+                /* must be a link, not just a port down, if we have a dst switch */
+                if (u.getDst() != null && !u.getDst().equals(DatapathId.NONE)) {
+                    /* dst side of link */
+                    IOFSwitch dstSw = switchService.getSwitch(u.getDst());
+                    if (dstSw != null) {
+                        /* flows matching on dst port */
+                        msgs.clear();
+                        msgs.add(dstSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setMatch(dstSw.getOFFactory().buildMatch()
+                                        .setExact(MatchField.IN_PORT, u.getDstPort())
+                                        .build())
+                                .build());
+                        /* flows outputting to dst port */
+                        msgs.add(dstSw.getOFFactory().buildFlowDelete()
+                                .setCookie(AppCookie.makeCookie(FORWARDING_APP_ID, 0))
+                                .setOutPort(u.getDstPort())
+                                .build());
+                        messageDamper.write(dstSw, msgs);
+                        log.warn("{}. Removing flows to/from DPID={}, port={}", new Object[] { u.getType(), u.getDst(), u.getDstPort() });
+                    }
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
index 471761e112f116b7493878ecc23d432925085ab9..b80444d854054679d26263bec2a5f1aad7f02c24 100644
--- a/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
+++ b/src/main/java/net/floodlightcontroller/routing/ForwardingBase.java
@@ -17,7 +17,6 @@
 
 package net.floodlightcontroller.routing;
 
-import java.io.IOException;
 import java.util.EnumSet;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -75,409 +74,398 @@ import org.slf4j.LoggerFactory;
  * decision.
  */
 public abstract class ForwardingBase implements IOFMessageListener {
-	protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class);
-
-	protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // TODO: find sweet spot
-	protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
-
-	public static int FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds
-	public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
-	public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1
-	
-	public static TableId FLOWMOD_DEFAULT_TABLE_ID = TableId.ZERO;
-
-	protected static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false;
-
-	protected static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true;
-	protected static boolean FLOWMOD_DEFAULT_MATCH_MAC = true;
-	protected static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true;
-	protected static boolean FLOWMOD_DEFAULT_MATCH_TRANSPORT = true;
-
-	protected static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT_CONSTANT = 5;
-	protected static final short FLOWMOD_DEFAULT_HARD_TIMEOUT_CONSTANT = 0;
-
-	protected static boolean FLOOD_ALL_ARP_PACKETS = false;
-
-	protected static boolean REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = true;
-
-	protected IFloodlightProviderService floodlightProviderService;
-	protected IOFSwitchService switchService;
-	protected IDeviceService deviceManagerService;
-	protected IRoutingService routingEngineService;
-	protected ITopologyService topologyService;
-	protected IDebugCounterService debugCounterService;
-	protected ILinkDiscoveryService linkService;
-
-	protected OFMessageDamper messageDamper;
-
-	// for broadcast loop suppression
-	protected boolean broadcastCacheFeature = true;
-	public final int prime1 = 2633;  // for hash calculation
-	public final static int prime2 = 4357;  // for hash calculation
-	public TimedCache<Long> broadcastCache = new TimedCache<Long>(100, 5*1000);  // 5 seconds interval;
-
-	// flow-mod - for use in the cookie
-	public static final int FORWARDING_APP_ID = 2; // TODO: This must be managed
-	// by a global APP_ID class
-	static {
-		AppCookie.registerApp(FORWARDING_APP_ID, "Forwarding");
-	}
-	public static final U64 appCookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
-
-	// Comparator for sorting by SwitchCluster
-	public Comparator<SwitchPort> clusterIdComparator =
-			new Comparator<SwitchPort>() {
-		@Override
-		public int compare(SwitchPort d1, SwitchPort d2) {
-			DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getNodeId());
-			DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getNodeId());
-			return d1ClusterId.compareTo(d2ClusterId);
-		}
-	};
-
-	protected void init() {
-		messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
-				EnumSet.of(OFType.FLOW_MOD),
-				OFMESSAGE_DAMPER_TIMEOUT);
-	}
-
-	protected void startUp() {
-		floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
-	}
-
-	@Override
-	public String getName() {
-		return "forwarding";
-	}
-
-	/**
-	 * All subclasses must define this function if they want any specific
-	 * forwarding action
-	 *
-	 * @param sw
-	 *            Switch that the packet came in from
-	 * @param pi
-	 *            The packet that came in
-	 * @param decision
-	 *            Any decision made by a policy engine
-	 */
-	public abstract Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, 
-			IRoutingDecision decision, FloodlightContext cntx);
-
-	@Override
-	public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
-		switch (msg.getType()) {
-		case PACKET_IN:
-			IRoutingDecision decision = null;
-			if (cntx != null) {
-				decision = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
-			}
-
-			return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx);
-		default:
-			break;
-		}
-		return Command.CONTINUE;
-	}
-
-	/**
-	 * Push routes from back to front
-	 * @param route Route to push
-	 * @param match OpenFlow fields to match on
-	 * @param srcSwPort Source switch port for the first hop
-	 * @param dstSwPort Destination switch port for final hop
-	 * @param cookie The cookie to set in each flow_mod
-	 * @param cntx The floodlight context
-	 * @param requestFlowRemovedNotification if set to true then the switch would
-	 *        send a flow mod removal notification when the flow mod expires
-	 * @param flowModCommand flow mod. command to use, e.g. OFFlowMod.OFPFC_ADD,
-	 *        OFFlowMod.OFPFC_MODIFY etc.
-	 * @return true if a packet out was sent on the first-hop switch of this route
-	 */
-	public boolean pushRoute(Route route, Match match, OFPacketIn pi,
-			DatapathId pinSwitch, U64 cookie, FloodlightContext cntx,
-			boolean requestFlowRemovedNotification, OFFlowModCommand flowModCommand) {
-
-		boolean packetOutSent = false;
-
-		List<NodePortTuple> switchPortList = route.getPath();
-
-		for (int indx = switchPortList.size() - 1; indx > 0; indx -= 2) {
-			// indx and indx-1 will always have the same switch DPID.
-			DatapathId switchDPID = switchPortList.get(indx).getNodeId();
-			IOFSwitch sw = switchService.getSwitch(switchDPID);
-
-			if (sw == null) {
-				if (log.isWarnEnabled()) {
-					log.warn("Unable to push route, switch at DPID {} " + "not available", switchDPID);
-				}
-				return packetOutSent;
-			}
-
-			// need to build flow mod based on what type it is. Cannot set command later
-			OFFlowMod.Builder fmb;
-			switch (flowModCommand) {
-			case ADD:
-				fmb = sw.getOFFactory().buildFlowAdd();
-				break;
-			case DELETE:
-				fmb = sw.getOFFactory().buildFlowDelete();
-				break;
-			case DELETE_STRICT:
-				fmb = sw.getOFFactory().buildFlowDeleteStrict();
-				break;
-			case MODIFY:
-				fmb = sw.getOFFactory().buildFlowModify();
-				break;
-			default:
-				log.error("Could not decode OFFlowModCommand. Using MODIFY_STRICT. (Should another be used as the default?)");        
-			case MODIFY_STRICT:
-				fmb = sw.getOFFactory().buildFlowModifyStrict();
-				break;			
-			}
-
-			OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput();
-			List<OFAction> actions = new ArrayList<OFAction>();	
-			Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion());
-
-			// set input and output ports on the switch
-			OFPort outPort = switchPortList.get(indx).getPortId();
-			OFPort inPort = switchPortList.get(indx - 1).getPortId();
-			mb.setExact(MatchField.IN_PORT, inPort);
-			aob.setPort(outPort);
-			aob.setMaxLen(Integer.MAX_VALUE);
-			actions.add(aob.build());
-
-			if (FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG || requestFlowRemovedNotification) {
-				Set<OFFlowModFlags> flags = new HashSet<>();
-				flags.add(OFFlowModFlags.SEND_FLOW_REM);
-				fmb.setFlags(flags);
-			}
-
-			fmb.setMatch(mb.build())
-			.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
-			.setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
-			.setBufferId(OFBufferId.NO_BUFFER)
-			.setCookie(cookie)
-			.setOutPort(outPort)
-			.setPriority(FLOWMOD_DEFAULT_PRIORITY);
-
-			FlowModUtils.setActions(fmb, actions, sw);
-			
-			/* Configure for particular switch pipeline */
-			fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID);
-
-			try {
-				if (log.isTraceEnabled()) {
-					log.trace("Pushing Route flowmod routeIndx={} " +
-							"sw={} inPort={} outPort={}",
-							new Object[] {indx,
-									sw,
-									fmb.getMatch().get(MatchField.IN_PORT),
-									outPort });
-				}
-
-				if (OFDPAUtils.isOFDPASwitch(sw)) {
-					OFDPAUtils.addLearningSwitchFlow(sw, cookie, 
-							FLOWMOD_DEFAULT_PRIORITY, 
-							FLOWMOD_DEFAULT_HARD_TIMEOUT,
-							FLOWMOD_DEFAULT_IDLE_TIMEOUT,
-							fmb.getMatch(), 
-							null, // TODO how to determine output VLAN for lookup of L2 interface group
-							outPort);
-				} else {
-					messageDamper.write(sw, fmb.build());
-				}
-
-				/* Push the packet out the first hop switch */
-				if (sw.getId().equals(pinSwitch) &&
-						!fmb.getCommand().equals(OFFlowModCommand.DELETE) &&
-						!fmb.getCommand().equals(OFFlowModCommand.DELETE_STRICT)) {
-					/* Use the buffered packet at the switch, if there's one stored */
-					pushPacket(sw, pi, outPort, true, cntx);
-					packetOutSent = true;
-				}
-			} catch (IOException e) {
-				log.error("Failure writing flow mod", e);
-			}
-		}
-
-		return packetOutSent;
-	}
-
-	/**
-	 * Pushes a packet-out to a switch. The assumption here is that
-	 * the packet-in was also generated from the same switch. Thus, if the input
-	 * port of the packet-in and the outport are the same, the function will not
-	 * push the packet-out.
-	 * @param sw switch that generated the packet-in, and from which packet-out is sent
-	 * @param pi packet-in
-	 * @param outport output port
-	 * @param useBufferedPacket use the packet buffered at the switch, if possible
-	 * @param cntx context of the packet
-	 */
-	protected void pushPacket(IOFSwitch sw, OFPacketIn pi, OFPort outport, boolean useBufferedPacket, FloodlightContext cntx) {
-		if (pi == null) {
-			return;
-		}
-
-		// The assumption here is (sw) is the switch that generated the
-		// packet-in. If the input port is the same as output port, then
-		// the packet-out should be ignored.
-		if ((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)).equals(outport)) {
-			if (log.isDebugEnabled()) {
-				log.debug("Attempting to do packet-out to the same " +
-						"interface as packet-in. Dropping packet. " +
-						" SrcSwitch={}, pi={}",
-						new Object[]{sw, pi});
-				return;
-			}
-		}
-
-		if (log.isTraceEnabled()) {
-			log.trace("PacketOut srcSwitch={} pi={}",
-					new Object[] {sw, pi});
-		}
-
-		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-		List<OFAction> actions = new ArrayList<OFAction>();
-		actions.add(sw.getOFFactory().actions().output(outport, Integer.MAX_VALUE));
-		pob.setActions(actions);
-
-		/* Use packet in buffer if there is a buffer ID set */
-		if (useBufferedPacket) {
-			pob.setBufferId(pi.getBufferId()); /* will be NO_BUFFER if there isn't one */
-		} else {
-			pob.setBufferId(OFBufferId.NO_BUFFER);
-		}
-
-		if (pob.getBufferId().equals(OFBufferId.NO_BUFFER)) {
-			byte[] packetData = pi.getData();
-			pob.setData(packetData);
-		}
-
-		pob.setInPort((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)));
-
-		try {
-			messageDamper.write(sw, pob.build());
-		} catch (IOException e) {
-			log.error("Failure writing packet out", e);
-		}
-	}
-
-	/**
-	 * Write packetout message to sw with output actions to one or more
-	 * output ports with inPort/outPorts passed in.
-	 * @param packetData
-	 * @param sw
-	 * @param inPort
-	 * @param ports
-	 * @param cntx
-	 */
-	public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, 
-			OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
-		//setting actions
-		List<OFAction> actions = new ArrayList<OFAction>();
-
-		Iterator<OFPort> j = outPorts.iterator();
-
-		while (j.hasNext()) {
-			actions.add(sw.getOFFactory().actions().output(j.next(), 0));
-		}
-
-		OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
-		pob.setActions(actions);
-
-		pob.setBufferId(OFBufferId.NO_BUFFER);
-		pob.setInPort(inPort);
-
-		pob.setData(packetData);
-
-		try {
-			if (log.isTraceEnabled()) {
-				log.trace("write broadcast packet on switch-id={} " +
-						"interfaces={} packet-out={}",
-						new Object[] {sw.getId(), outPorts, pob.build()});
-			}
-			messageDamper.write(sw, pob.build());
-
-		} catch (IOException e) {
-			log.error("Failure writing packet out", e);
-		}
-	}
-
-	/**
-	 * @see packetOutMultiPort
-	 * Accepts a PacketIn instead of raw packet data. Note that the inPort
-	 * and switch can be different than the packet in switch/port
-	 */
-	public void packetOutMultiPort(OFPacketIn pi, IOFSwitch sw,
-			OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
-		packetOutMultiPort(pi.getData(), sw, inPort, outPorts, cntx);
-	}
-
-	/**
-	 * @see packetOutMultiPort
-	 * Accepts an IPacket instead of raw packet data. Note that the inPort
-	 * and switch can be different than the packet in switch/port
-	 */
-	public void packetOutMultiPort(IPacket packet, IOFSwitch sw,
-			OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
-		packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx);
-	}
-
-	public static boolean blockHost(IOFSwitchService switchService,
-			SwitchPort sw_tup, MacAddress host_mac, short hardTimeout, U64 cookie) {
-
-		if (sw_tup == null) {
-			return false;
-		}
-
-		IOFSwitch sw = switchService.getSwitch(sw_tup.getNodeId());
-		if (sw == null) {
-			return false;
-		}
-
-		OFPort inputPort = sw_tup.getPortId();
-		if (log.isDebugEnabled()) {
-			log.debug("blockHost sw={} port={} mac={}",
-					new Object[] { sw, sw_tup.getPortId(), host_mac.getLong() });
-		}
-
-		// Create flow-mod based on packet-in and src-switch
-		OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd();
-
-		Match.Builder mb = sw.getOFFactory().buildMatch();
-		List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop
-		mb.setExact(MatchField.IN_PORT, inputPort);
-		if (host_mac.getLong() != -1L) {
-			mb.setExact(MatchField.ETH_SRC, host_mac);
-		}
-
-		fmb.setCookie(cookie)
-		.setHardTimeout(hardTimeout)
-		.setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
-		.setPriority(FLOWMOD_DEFAULT_PRIORITY)
-		.setBufferId(OFBufferId.NO_BUFFER)
-		.setMatch(mb.build());
-
-		FlowModUtils.setActions(fmb, actions, sw);
-
-		log.debug("write drop flow-mod sw={} match={} flow-mod={}",
-				new Object[] { sw, mb.build(), fmb.build() });
-
-		sw.write(fmb.build());
-
-		return true;
-	}
-
-	@Override
-	public boolean isCallbackOrderingPrereq(OFType type, String name) {
-		return (type.equals(OFType.PACKET_IN) && (name.equals("topology") || name.equals("devicemanager")));
-	}
-
-	@Override
-	public boolean isCallbackOrderingPostreq(OFType type, String name) {
-		return false;
-	}
+    protected static Logger log = LoggerFactory.getLogger(ForwardingBase.class);
+
+    protected static int OFMESSAGE_DAMPER_CAPACITY = 10000; // TODO: find sweet spot
+    protected static int OFMESSAGE_DAMPER_TIMEOUT = 250; // ms
+
+    public static int FLOWMOD_DEFAULT_IDLE_TIMEOUT = 5; // in seconds
+    public static int FLOWMOD_DEFAULT_HARD_TIMEOUT = 0; // infinite
+    public static int FLOWMOD_DEFAULT_PRIORITY = 1; // 0 is the default table-miss flow in OF1.3+, so we need to use 1
+
+    public static TableId FLOWMOD_DEFAULT_TABLE_ID = TableId.ZERO;
+
+    protected static boolean FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG = false;
+
+    protected static boolean FLOWMOD_DEFAULT_MATCH_VLAN = true;
+    protected static boolean FLOWMOD_DEFAULT_MATCH_MAC = true;
+    protected static boolean FLOWMOD_DEFAULT_MATCH_IP_ADDR = true;
+    protected static boolean FLOWMOD_DEFAULT_MATCH_TRANSPORT = true;
+
+    protected static final short FLOWMOD_DEFAULT_IDLE_TIMEOUT_CONSTANT = 5;
+    protected static final short FLOWMOD_DEFAULT_HARD_TIMEOUT_CONSTANT = 0;
+
+    protected static boolean FLOOD_ALL_ARP_PACKETS = false;
+
+    protected static boolean REMOVE_FLOWS_ON_LINK_OR_PORT_DOWN = true;
+
+    protected IFloodlightProviderService floodlightProviderService;
+    protected IOFSwitchService switchService;
+    protected IDeviceService deviceManagerService;
+    protected IRoutingService routingEngineService;
+    protected ITopologyService topologyService;
+    protected IDebugCounterService debugCounterService;
+    protected ILinkDiscoveryService linkService;
+
+    protected OFMessageDamper messageDamper;
+
+    // for broadcast loop suppression
+    protected boolean broadcastCacheFeature = true;
+    public final int prime1 = 2633;  // for hash calculation
+    public final static int prime2 = 4357;  // for hash calculation
+    public TimedCache<Long> broadcastCache = new TimedCache<Long>(100, 5*1000);  // 5 seconds interval;
+
+    // flow-mod - for use in the cookie
+    public static final int FORWARDING_APP_ID = 2; // TODO: This must be managed
+    // by a global APP_ID class
+    static {
+        AppCookie.registerApp(FORWARDING_APP_ID, "Forwarding");
+    }
+    public static final U64 appCookie = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
+
+    // Comparator for sorting by SwitchCluster
+    public Comparator<SwitchPort> clusterIdComparator =
+            new Comparator<SwitchPort>() {
+        @Override
+        public int compare(SwitchPort d1, SwitchPort d2) {
+            DatapathId d1ClusterId = topologyService.getOpenflowDomainId(d1.getNodeId());
+            DatapathId d2ClusterId = topologyService.getOpenflowDomainId(d2.getNodeId());
+            return d1ClusterId.compareTo(d2ClusterId);
+        }
+    };
+
+    protected void init() {
+        messageDamper = new OFMessageDamper(OFMESSAGE_DAMPER_CAPACITY,
+                EnumSet.of(OFType.FLOW_MOD),
+                OFMESSAGE_DAMPER_TIMEOUT);
+    }
+
+    protected void startUp() {
+        floodlightProviderService.addOFMessageListener(OFType.PACKET_IN, this);
+    }
+
+    @Override
+    public String getName() {
+        return "forwarding";
+    }
+
+    /**
+     * All subclasses must define this function if they want any specific
+     * forwarding action
+     *
+     * @param sw
+     *            Switch that the packet came in from
+     * @param pi
+     *            The packet that came in
+     * @param decision
+     *            Any decision made by a policy engine
+     */
+    public abstract Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, 
+            IRoutingDecision decision, FloodlightContext cntx);
+
+    @Override
+    public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
+        switch (msg.getType()) {
+        case PACKET_IN:
+            IRoutingDecision decision = null;
+            if (cntx != null) {
+                decision = RoutingDecision.rtStore.get(cntx, IRoutingDecision.CONTEXT_DECISION);
+            }
+
+            return this.processPacketInMessage(sw, (OFPacketIn) msg, decision, cntx);
+        default:
+            break;
+        }
+        return Command.CONTINUE;
+    }
+
+    /**
+     * Push routes from back to front
+     * @param route Route to push
+     * @param match OpenFlow fields to match on
+     * @param srcSwPort Source switch port for the first hop
+     * @param dstSwPort Destination switch port for final hop
+     * @param cookie The cookie to set in each flow_mod
+     * @param cntx The floodlight context
+     * @param requestFlowRemovedNotification if set to true then the switch would
+     *        send a flow mod removal notification when the flow mod expires
+     * @param flowModCommand flow mod. command to use, e.g. OFFlowMod.OFPFC_ADD,
+     *        OFFlowMod.OFPFC_MODIFY etc.
+     * @return true if a packet out was sent on the first-hop switch of this route
+     */
+    public boolean pushRoute(Route route, Match match, OFPacketIn pi,
+            DatapathId pinSwitch, U64 cookie, FloodlightContext cntx,
+            boolean requestFlowRemovedNotification, OFFlowModCommand flowModCommand) {
+
+        boolean packetOutSent = false;
+
+        List<NodePortTuple> switchPortList = route.getPath();
+
+        for (int indx = switchPortList.size() - 1; indx > 0; indx -= 2) {
+            // indx and indx-1 will always have the same switch DPID.
+            DatapathId switchDPID = switchPortList.get(indx).getNodeId();
+            IOFSwitch sw = switchService.getSwitch(switchDPID);
+
+            if (sw == null) {
+                if (log.isWarnEnabled()) {
+                    log.warn("Unable to push route, switch at DPID {} " + "not available", switchDPID);
+                }
+                return packetOutSent;
+            }
+
+            // need to build flow mod based on what type it is. Cannot set command later
+            OFFlowMod.Builder fmb;
+            switch (flowModCommand) {
+            case ADD:
+                fmb = sw.getOFFactory().buildFlowAdd();
+                break;
+            case DELETE:
+                fmb = sw.getOFFactory().buildFlowDelete();
+                break;
+            case DELETE_STRICT:
+                fmb = sw.getOFFactory().buildFlowDeleteStrict();
+                break;
+            case MODIFY:
+                fmb = sw.getOFFactory().buildFlowModify();
+                break;
+            default:
+                log.error("Could not decode OFFlowModCommand. Using MODIFY_STRICT. (Should another be used as the default?)");        
+            case MODIFY_STRICT:
+                fmb = sw.getOFFactory().buildFlowModifyStrict();
+                break;			
+            }
+
+            OFActionOutput.Builder aob = sw.getOFFactory().actions().buildOutput();
+            List<OFAction> actions = new ArrayList<OFAction>();	
+            Match.Builder mb = MatchUtils.convertToVersion(match, sw.getOFFactory().getVersion());
+
+            // set input and output ports on the switch
+            OFPort outPort = switchPortList.get(indx).getPortId();
+            OFPort inPort = switchPortList.get(indx - 1).getPortId();
+            mb.setExact(MatchField.IN_PORT, inPort);
+            aob.setPort(outPort);
+            aob.setMaxLen(Integer.MAX_VALUE);
+            actions.add(aob.build());
+
+            if (FLOWMOD_DEFAULT_SET_SEND_FLOW_REM_FLAG || requestFlowRemovedNotification) {
+                Set<OFFlowModFlags> flags = new HashSet<>();
+                flags.add(OFFlowModFlags.SEND_FLOW_REM);
+                fmb.setFlags(flags);
+            }
+
+            fmb.setMatch(mb.build())
+            .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+            .setHardTimeout(FLOWMOD_DEFAULT_HARD_TIMEOUT)
+            .setBufferId(OFBufferId.NO_BUFFER)
+            .setCookie(cookie)
+            .setOutPort(outPort)
+            .setPriority(FLOWMOD_DEFAULT_PRIORITY);
+
+            FlowModUtils.setActions(fmb, actions, sw);
+
+            /* Configure for particular switch pipeline */
+            fmb.setTableId(FLOWMOD_DEFAULT_TABLE_ID);
+
+            if (log.isTraceEnabled()) {
+                log.trace("Pushing Route flowmod routeIndx={} " +
+                        "sw={} inPort={} outPort={}",
+                        new Object[] {indx,
+                                sw,
+                                fmb.getMatch().get(MatchField.IN_PORT),
+                                outPort });
+            }
+
+            if (OFDPAUtils.isOFDPASwitch(sw)) {
+                OFDPAUtils.addLearningSwitchFlow(sw, cookie, 
+                        FLOWMOD_DEFAULT_PRIORITY, 
+                        FLOWMOD_DEFAULT_HARD_TIMEOUT,
+                        FLOWMOD_DEFAULT_IDLE_TIMEOUT,
+                        fmb.getMatch(), 
+                        null, // TODO how to determine output VLAN for lookup of L2 interface group
+                        outPort);
+            } else {
+                messageDamper.write(sw, fmb.build());
+            }
+
+            /* Push the packet out the first hop switch */
+            if (sw.getId().equals(pinSwitch) &&
+                    !fmb.getCommand().equals(OFFlowModCommand.DELETE) &&
+                    !fmb.getCommand().equals(OFFlowModCommand.DELETE_STRICT)) {
+                /* Use the buffered packet at the switch, if there's one stored */
+                pushPacket(sw, pi, outPort, true, cntx);
+                packetOutSent = true;
+            }
+        }
+
+        return packetOutSent;
+    }
+
+    /**
+     * Pushes a packet-out to a switch. The assumption here is that
+     * the packet-in was also generated from the same switch. Thus, if the input
+     * port of the packet-in and the outport are the same, the function will not
+     * push the packet-out.
+     * @param sw switch that generated the packet-in, and from which packet-out is sent
+     * @param pi packet-in
+     * @param outport output port
+     * @param useBufferedPacket use the packet buffered at the switch, if possible
+     * @param cntx context of the packet
+     */
+    protected void pushPacket(IOFSwitch sw, OFPacketIn pi, OFPort outport, boolean useBufferedPacket, FloodlightContext cntx) {
+        if (pi == null) {
+            return;
+        }
+
+        // The assumption here is (sw) is the switch that generated the
+        // packet-in. If the input port is the same as output port, then
+        // the packet-out should be ignored.
+        if ((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)).equals(outport)) {
+            if (log.isDebugEnabled()) {
+                log.debug("Attempting to do packet-out to the same " +
+                        "interface as packet-in. Dropping packet. " +
+                        " SrcSwitch={}, pi={}",
+                        new Object[]{sw, pi});
+                return;
+            }
+        }
+
+        if (log.isTraceEnabled()) {
+            log.trace("PacketOut srcSwitch={} pi={}",
+                    new Object[] {sw, pi});
+        }
+
+        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+        List<OFAction> actions = new ArrayList<OFAction>();
+        actions.add(sw.getOFFactory().actions().output(outport, Integer.MAX_VALUE));
+        pob.setActions(actions);
+
+        /* Use packet in buffer if there is a buffer ID set */
+        if (useBufferedPacket) {
+            pob.setBufferId(pi.getBufferId()); /* will be NO_BUFFER if there isn't one */
+        } else {
+            pob.setBufferId(OFBufferId.NO_BUFFER);
+        }
+
+        if (pob.getBufferId().equals(OFBufferId.NO_BUFFER)) {
+            byte[] packetData = pi.getData();
+            pob.setData(packetData);
+        }
+
+        pob.setInPort((pi.getVersion().compareTo(OFVersion.OF_12) < 0 ? pi.getInPort() : pi.getMatch().get(MatchField.IN_PORT)));
+
+        messageDamper.write(sw, pob.build());
+    }
+
+    /**
+     * Write packetout message to sw with output actions to one or more
+     * output ports with inPort/outPorts passed in.
+     * @param packetData
+     * @param sw
+     * @param inPort
+     * @param ports
+     * @param cntx
+     */
+    public void packetOutMultiPort(byte[] packetData, IOFSwitch sw, 
+            OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
+        //setting actions
+        List<OFAction> actions = new ArrayList<OFAction>();
+
+        Iterator<OFPort> j = outPorts.iterator();
+
+        while (j.hasNext()) {
+            actions.add(sw.getOFFactory().actions().output(j.next(), 0));
+        }
+
+        OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut();
+        pob.setActions(actions);
+
+        pob.setBufferId(OFBufferId.NO_BUFFER);
+        pob.setInPort(inPort);
+
+        pob.setData(packetData);
+
+        if (log.isTraceEnabled()) {
+            log.trace("write broadcast packet on switch-id={} " +
+                    "interfaces={} packet-out={}",
+                    new Object[] {sw.getId(), outPorts, pob.build()});
+        }
+        messageDamper.write(sw, pob.build());
+    }
+
+    /**
+     * @see packetOutMultiPort
+     * Accepts a PacketIn instead of raw packet data. Note that the inPort
+     * and switch can be different than the packet in switch/port
+     */
+    public void packetOutMultiPort(OFPacketIn pi, IOFSwitch sw,
+            OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
+        packetOutMultiPort(pi.getData(), sw, inPort, outPorts, cntx);
+    }
+
+    /**
+     * @see packetOutMultiPort
+     * Accepts an IPacket instead of raw packet data. Note that the inPort
+     * and switch can be different than the packet in switch/port
+     */
+    public void packetOutMultiPort(IPacket packet, IOFSwitch sw,
+            OFPort inPort, Set<OFPort> outPorts, FloodlightContext cntx) {
+        packetOutMultiPort(packet.serialize(), sw, inPort, outPorts, cntx);
+    }
+
+    public boolean blockHost(IOFSwitchService switchService,
+            SwitchPort sw_tup, MacAddress host_mac, short hardTimeout, U64 cookie) {
+
+        if (sw_tup == null) {
+            return false;
+        }
+
+        IOFSwitch sw = switchService.getSwitch(sw_tup.getNodeId());
+        if (sw == null) {
+            return false;
+        }
+
+        OFPort inputPort = sw_tup.getPortId();
+        if (log.isDebugEnabled()) {
+            log.debug("blockHost sw={} port={} mac={}",
+                    new Object[] { sw, sw_tup.getPortId(), host_mac.getLong() });
+        }
+
+        // Create flow-mod based on packet-in and src-switch
+        OFFlowMod.Builder fmb = sw.getOFFactory().buildFlowAdd();
+
+        Match.Builder mb = sw.getOFFactory().buildMatch();
+        List<OFAction> actions = new ArrayList<OFAction>(); // Set no action to drop
+        mb.setExact(MatchField.IN_PORT, inputPort);
+        if (host_mac.getLong() != -1L) {
+            mb.setExact(MatchField.ETH_SRC, host_mac);
+        }
+
+        fmb.setCookie(cookie)
+        .setHardTimeout(hardTimeout)
+        .setIdleTimeout(FLOWMOD_DEFAULT_IDLE_TIMEOUT)
+        .setPriority(FLOWMOD_DEFAULT_PRIORITY)
+        .setBufferId(OFBufferId.NO_BUFFER)
+        .setMatch(mb.build());
+
+        FlowModUtils.setActions(fmb, actions, sw);
+
+        if (log.isDebugEnabled()) {
+            log.debug("write drop flow-mod sw={} match={} flow-mod={}",
+                    new Object[] { sw, mb.build(), fmb.build() });
+        }
+
+        messageDamper.write(sw, fmb.build());
+
+        return true;
+    }
+
+    @Override
+    public boolean isCallbackOrderingPrereq(OFType type, String name) {
+        return (type.equals(OFType.PACKET_IN) && (name.equals("topology") || name.equals("devicemanager")));
+    }
+
+    @Override
+    public boolean isCallbackOrderingPostreq(OFType type, String name) {
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
index 047ffa06feb13953184c9ff40d0362b8ea66d196..7e53a5ad0180208facaad7226ddb3ea65256783a 100644
--- a/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
+++ b/src/main/java/net/floodlightcontroller/util/OFMessageDamper.java
@@ -16,7 +16,7 @@
 
 package net.floodlightcontroller.util;
 
-import java.io.IOException;
+import java.util.Collection;
 import java.util.EnumSet;
 import java.util.Set;
 
@@ -116,9 +116,8 @@ public class OFMessageDamper {
      * @param msg
      * @return true if the message was written to the switch, false if
      * the message was dampened. 
-     * @throws IOException
      */
-    public boolean write(IOFSwitch sw, OFMessage msg) throws IOException {
+    public boolean write(IOFSwitch sw, OFMessage msg) {
         if (!msgTypesToCache.contains(msg.getType())) {
             log.debug("Not dampening this type of msg {}", msg);
             sw.write(msg);
@@ -136,4 +135,20 @@ public class OFMessageDamper {
             return true;
         }
     }
-}
+    
+    /**
+     * Wrapper around {@link OFMessageDamper#write(IOFSwitch, OFMessage)}. 
+     * @param sw
+     * @param msgs
+     * @return false if *any* message was dampened; true if no messages were dampened
+     */
+    public boolean write(IOFSwitch sw, Collection<OFMessage> msgs) {
+        boolean allWritten = true;
+        for (OFMessage msg : msgs) {
+            if (!write(sw, msg)) {
+                allWritten = false;
+            }
+        }
+        return allWritten;
+    }
+}
\ No newline at end of file