Moving openflow to protocols/ directory

Change-Id: Ie7695f4ab25e9906ebf2ec1a0b59f74d652089b8
diff --git a/protocols/openflow/api/pom.xml b/protocols/openflow/api/pom.xml
new file mode 100644
index 0000000..e535ac6
--- /dev/null
+++ b/protocols/openflow/api/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-of</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-of-api</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS OpenFlow controller subsystem API</description>
+
+    <dependencies>
+        <dependency>
+            <!-- FIXME once experimenter gets merged to upstream -->
+            <groupId>org.onosproject</groupId>
+            <artifactId>openflowj</artifactId>
+            <version>${openflowj.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-api</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.3</version>
+                <configuration>
+                    <artifactSet>
+                        <excludes>
+                            <exclude>io.netty:netty</exclude>
+                            <exclude>com.google.guava:guava</exclude>
+                            <exclude>org.slf4j:slfj-api</exclude>
+                            <exclude>ch.qos.logback:logback-core</exclude>
+                            <exclude>ch.qos.logback:logback-classic</exclude>
+                            <exclude>com.google.code.findbugs:annotations</exclude>
+                        </excludes>
+                    </artifactSet>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.onosproject.openflow.*,org.projectfloodlight.openflow.*
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
new file mode 100644
index 0000000..af92a1d
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Ethernet;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPacketOut;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.BufferUnderflowException;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Default implementation of an OpenFlowPacketContext.
+ */
+public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext {
+
+    private final AtomicBoolean free = new AtomicBoolean(true);
+    private final AtomicBoolean isBuilt = new AtomicBoolean(false);
+    private final OpenFlowSwitch sw;
+    private final OFPacketIn pktin;
+    private OFPacketOut pktout = null;
+
+    private final boolean isBuffered;
+
+    private DefaultOpenFlowPacketContext(OpenFlowSwitch s, OFPacketIn pkt) {
+        this.sw = s;
+        this.pktin = pkt;
+        this.isBuffered = pktin.getBufferId() != OFBufferId.NO_BUFFER;
+    }
+
+    @Override
+    public void send() {
+        checkPermission(PACKET_WRITE);
+
+        if (block() && isBuilt.get()) {
+            sw.sendMsg(pktout);
+        }
+    }
+
+    @Override
+    public void build(OFPort outPort) {
+        if (isBuilt.getAndSet(true)) {
+            return;
+        }
+        OFPacketOut.Builder builder = sw.factory().buildPacketOut();
+        OFAction act = buildOutput(outPort.getPortNumber());
+        pktout = builder.setXid(pktin.getXid())
+                .setInPort(pktinInPort())
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setData(pktin.getData())
+//                .setBufferId(pktin.getBufferId())
+                .setActions(Collections.singletonList(act))
+                .build();
+    }
+
+    @Override
+    public void build(Ethernet ethFrame, OFPort outPort) {
+        if (isBuilt.getAndSet(true)) {
+            return;
+        }
+        OFPacketOut.Builder builder = sw.factory().buildPacketOut();
+        OFAction act = buildOutput(outPort.getPortNumber());
+        pktout = builder.setXid(pktin.getXid())
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setInPort(pktinInPort())
+                .setActions(Collections.singletonList(act))
+                .setData(ethFrame.serialize())
+                .build();
+    }
+
+    @Override
+    public Ethernet parsed() {
+        checkPermission(PACKET_READ);
+
+        try {
+            return Ethernet.deserializer().deserialize(pktin.getData(), 0, pktin.getData().length);
+        } catch (BufferUnderflowException | NullPointerException |
+                DeserializationException e) {
+            Logger log = LoggerFactory.getLogger(getClass());
+            log.error("packet deserialization problem : {}", e.getMessage());
+            return null;
+        }
+    }
+
+    @Override
+    public Dpid dpid() {
+        checkPermission(PACKET_READ);
+
+        return new Dpid(sw.getId());
+    }
+
+    /**
+     * Creates an OpenFlow packet context based on a packet-in.
+     *
+     * @param s OpenFlow switch
+     * @param pkt OpenFlow packet-in
+     * @return the OpenFlow packet context
+     */
+    public static OpenFlowPacketContext packetContextFromPacketIn(OpenFlowSwitch s,
+                                                                  OFPacketIn pkt) {
+        return new DefaultOpenFlowPacketContext(s, pkt);
+    }
+
+    @Override
+    public Integer inPort() {
+        checkPermission(PACKET_READ);
+
+        return pktinInPort().getPortNumber();
+    }
+
+    private OFPort pktinInPort() {
+        if (pktin.getVersion() == OFVersion.OF_10) {
+            return pktin.getInPort();
+        }
+        return pktin.getMatch().get(MatchField.IN_PORT);
+    }
+
+    @Override
+    public byte[] unparsed() {
+        checkPermission(PACKET_READ);
+
+        return pktin.getData().clone();
+
+    }
+
+    private OFActionOutput buildOutput(Integer port) {
+        OFActionOutput act = sw.factory().actions()
+                .buildOutput()
+                .setPort(OFPort.of(port))
+                .build();
+        return act;
+    }
+
+    @Override
+    public boolean block() {
+        checkPermission(PACKET_WRITE);
+
+        return free.getAndSet(false);
+    }
+
+    @Override
+    public boolean isHandled() {
+        checkPermission(PACKET_READ);
+
+        return !free.get();
+    }
+
+    @Override
+    public boolean isBuffered() {
+        checkPermission(PACKET_READ);
+
+        return isBuffered;
+    }
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/Dpid.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/Dpid.java
new file mode 100644
index 0000000..6e0f65b
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/Dpid.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.projectfloodlight.openflow.util.HexString;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onlab.util.Tools.fromHex;
+import static org.onlab.util.Tools.toHex;
+
+/**
+ * The class representing a network switch DPID.
+ * This class is immutable.
+ */
+public final class Dpid {
+
+    private static final String SCHEME = "of";
+    private static final long UNKNOWN = 0;
+    private final long value;
+
+    /**
+     * Default constructor.
+     */
+    public Dpid() {
+        this.value = Dpid.UNKNOWN;
+    }
+
+    /**
+     * Constructor from a long value.
+     *
+     * @param value the value to use.
+     */
+    public Dpid(long value) {
+        this.value = value;
+    }
+
+    /**
+     * Constructor from a string.
+     *
+     * @param value the value to use.
+     */
+    public Dpid(String value) {
+        this.value = HexString.toLong(value);
+    }
+
+    /**
+     * Get the value of the DPID.
+     *
+     * @return the value of the DPID.
+     */
+    public long value() {
+        return value;
+    }
+
+    /**
+     * Convert the DPID value to a ':' separated hexadecimal string.
+     *
+     * @return the DPID value as a ':' separated hexadecimal string.
+     */
+    @Override
+    public String toString() {
+        return HexString.toHexString(this.value);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof Dpid)) {
+            return false;
+        }
+
+        Dpid otherDpid = (Dpid) other;
+
+        return value == otherDpid.value;
+    }
+
+    @Override
+    public int hashCode() {
+        return Long.hashCode(value);
+    }
+
+    /**
+     * Returns DPID created from the given device URI.
+     *
+     * @param uri device URI
+     * @return dpid
+     */
+    public static Dpid dpid(URI uri) {
+        checkArgument(uri.getScheme().equals(SCHEME), "Unsupported URI scheme");
+        return new Dpid(fromHex(uri.getSchemeSpecificPart()));
+    }
+
+    /**
+     * Produces device URI from the given DPID.
+     *
+     * @param dpid device dpid
+     * @return device URI
+     */
+    public static URI uri(Dpid dpid) {
+        return uri(dpid.value);
+    }
+
+    /**
+     * Produces device URI from the given DPID long.
+     *
+     * @param value device dpid as long
+     * @return device URI
+     */
+    public static URI uri(long value) {
+        try {
+            return new URI(SCHEME, toHex(value), null);
+        } catch (URISyntaxException e) {
+            return null;
+        }
+    }
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ExtensionInterpreter.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ExtensionInterpreter.java
new file mode 100644
index 0000000..44b121a
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ExtensionInterpreter.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.driver.HandlerBehaviour;
+import org.onosproject.net.flow.instructions.ExtensionInstruction;
+import org.onosproject.net.flow.instructions.ExtensionType;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+
+/**
+ * Interprets extension instructions and converts them to/from OpenFlow objects.
+ */
+@Beta
+public interface ExtensionInterpreter extends HandlerBehaviour {
+
+    /**
+     * Returns true if the given extension instruction is supported by this
+     * driver.
+     *
+     * @param extensionType extension instruction type
+     * @return true if the instruction is supported, otherwise false
+     */
+    boolean supported(ExtensionType extensionType);
+
+    /**
+     * Maps an extension instruction to an OpenFlow action.
+     *
+     * @param factory OpenFlow factory
+     * @param extensionInstruction extension instruction
+     * @return OpenFlow action
+     */
+    OFAction mapInstruction(OFFactory factory, ExtensionInstruction extensionInstruction);
+
+    /**
+     * Maps an OpenFlow action to an extension instruction.
+     *
+     * @param action OpenFlow action
+     * @return extension instruction
+     */
+    ExtensionInstruction mapAction(OFAction action);
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowController.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowController.java
new file mode 100644
index 0000000..2c68fa0
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowController.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.projectfloodlight.openflow.protocol.OFMessage;
+
+/**
+ * Abstraction of an OpenFlow controller. Serves as a one stop
+ * shop for obtaining OpenFlow devices and (un)register listeners
+ * on OpenFlow events
+ */
+public interface OpenFlowController {
+
+    /**
+     * Returns all switches known to this OF controller.
+     * @return Iterable of dpid elements
+     */
+    Iterable<OpenFlowSwitch> getSwitches();
+
+    /**
+     * Returns all master switches known to this OF controller.
+     * @return Iterable of dpid elements
+     */
+    Iterable<OpenFlowSwitch> getMasterSwitches();
+
+    /**
+     * Returns all equal switches known to this OF controller.
+     * @return Iterable of dpid elements
+     */
+    Iterable<OpenFlowSwitch> getEqualSwitches();
+
+
+    /**
+     * Returns the actual switch for the given Dpid.
+     * @param dpid the switch to fetch
+     * @return the interface to this switch
+     */
+    OpenFlowSwitch getSwitch(Dpid dpid);
+
+    /**
+     * Returns the actual master switch for the given Dpid, if one exists.
+     * @param dpid the switch to fetch
+     * @return the interface to this switch
+     */
+    OpenFlowSwitch getMasterSwitch(Dpid dpid);
+
+    /**
+     * Returns the actual equal switch for the given Dpid, if one exists.
+     * @param dpid the switch to fetch
+     * @return the interface to this switch
+     */
+    OpenFlowSwitch getEqualSwitch(Dpid dpid);
+
+    /**
+     * Register a listener for meta events that occur to OF
+     * devices.
+     * @param listener the listener to notify
+     */
+    void addListener(OpenFlowSwitchListener listener);
+
+    /**
+     * Unregister a listener.
+     *
+     * @param listener the listener to unregister
+     */
+    void removeListener(OpenFlowSwitchListener listener);
+
+    /**
+     * Register a listener for packet events.
+     * @param priority the importance of this listener, lower values are more important
+     * @param listener the listener to notify
+     */
+    void addPacketListener(int priority, PacketListener listener);
+
+    /**
+     * Unregister a listener.
+     *
+     * @param listener the listener to unregister
+     */
+    void removePacketListener(PacketListener listener);
+
+    /**
+     * Register a listener for OF msg events.
+     *
+     * @param listener the listener to notify
+     */
+    void addEventListener(OpenFlowEventListener listener);
+
+    /**
+     * Unregister a listener.
+     *
+     * @param listener the listener to unregister
+     */
+    void removeEventListener(OpenFlowEventListener listener);
+
+    /**
+     * Send a message to a particular switch.
+     * @param dpid the switch to send to.
+     * @param msg the message to send
+     */
+    void write(Dpid dpid, OFMessage msg);
+
+    /**
+     * Process a message and notify the appropriate listeners.
+     *
+     * @param dpid the dpid the message arrived on
+     * @param msg the message to process.
+     */
+    void processPacket(Dpid dpid, OFMessage msg);
+
+    /**
+     * Sets the role for a given switch.
+     * @param role the desired role
+     * @param dpid the switch to set the role for.
+     */
+    void setRole(Dpid dpid, RoleState role);
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowEventListener.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowEventListener.java
new file mode 100644
index 0000000..5deccf5
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowEventListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.projectfloodlight.openflow.protocol.OFMessage;
+
+
+/**
+ * Notifies providers about openflow msg events.
+ */
+public interface OpenFlowEventListener {
+
+    /**
+     * Handles the message event.
+     *
+     * @param dpid switch data path identifier
+     * @param msg the message
+     */
+    void handleMessage(Dpid dpid, OFMessage msg);
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
new file mode 100644
index 0000000..af678d6
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowOpticalSwitch.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+/**
+ * A marker interface for optical switches, which require the ability to pass
+ * port information to a Device provider.
+ */
+public interface OpenFlowOpticalSwitch extends OpenFlowSwitch, WithTypedPorts {
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowPacketContext.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowPacketContext.java
new file mode 100644
index 0000000..740d89d
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowPacketContext.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.onlab.packet.Ethernet;
+import org.projectfloodlight.openflow.types.OFPort;
+
+/**
+ * A representation of a packet context which allows any provider
+ * to view a packet in event, but may block the response to the
+ * event if blocked has been called. This packet context can be used
+ * to react to the packet in event with a packet out.
+ */
+public interface OpenFlowPacketContext {
+
+    /**
+     * Blocks further responses (ie. send() calls) on this
+     * packet in event.
+     * @return true if blocks
+     */
+    boolean block();
+
+    /**
+     * Checks whether the packet has been handled.
+     * @return true if handled, false otherwise.
+     */
+    boolean isHandled();
+
+    /**
+     * Provided build has been called send the packet
+     * out the switch it came in on.
+     */
+    void send();
+
+    /**
+     * Build the packet out in response to this packet in event.
+     * @param outPort the out port to send to packet out of.
+     */
+    void build(OFPort outPort);
+
+    /**
+     * Build the packet out in response to this packet in event.
+     * @param ethFrame the actual packet to send out.
+     * @param outPort the out port to send to packet out of.
+     */
+    void build(Ethernet ethFrame, OFPort outPort);
+
+    /**
+     * Provided a handle onto the parsed payload.
+     * @return the parsed form of the payload.
+     */
+    Ethernet parsed();
+
+    /**
+     * Provide an unparsed copy of the data.
+     * @return the unparsed form of the payload.
+     */
+    byte[] unparsed();
+
+    /**
+     * Provide the dpid of the switch where the packet in arrived.
+     * @return the dpid of the switch.
+     */
+    Dpid dpid();
+
+    /**
+     * Provide the port on which the packet arrived.
+     * @return the port
+     */
+    Integer inPort();
+
+    /**
+     * Indicates that this packet is buffered at the switch.
+     * @return buffer indication
+     */
+    boolean isBuffered();
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitch.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitch.java
new file mode 100644
index 0000000..b6ec574
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitch.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.onosproject.net.Device;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+
+import java.util.List;
+
+/**
+ * Represents to provider facing side of a switch.
+ */
+public interface OpenFlowSwitch {
+
+    /**
+     * Writes the message to the driver.
+     * <p>
+     * Note: Messages may be silently dropped/lost due to IOExceptions or
+     * role. If this is a concern, then a caller should use barriers.
+     * </p>
+     *
+     * @param msg the message to write
+     */
+    void sendMsg(OFMessage msg);
+
+    /**
+     * Writes the OFMessage list to the driver.
+     * <p>
+     * Note: Messages may be silently dropped/lost due to IOExceptions or
+     * role. If this is a concern, then a caller should use barriers.
+     * </p>
+     *
+     * @param msgs the messages to be written
+     */
+    void sendMsg(List<OFMessage> msgs);
+
+    /**
+     * Handle a message from the switch.
+     * @param fromSwitch the message to handle
+     */
+    void handleMessage(OFMessage fromSwitch);
+
+    /**
+     * Sets the role for this switch.
+     * @param role the role to set.
+     */
+    void setRole(RoleState role);
+
+    /**
+     * Fetch the role for this switch.
+     * @return the role.
+     */
+    RoleState getRole();
+
+    /**
+     * Fetches the ports of this switch.
+     * @return unmodifiable list of the ports.
+     */
+    List<OFPortDesc> getPorts();
+
+    /**
+     * Provides the factory for this OF version.
+     * @return OF version specific factory.
+     */
+    OFFactory factory();
+
+    /**
+     * Gets a string version of the ID for this switch.
+     *
+     * @return string version of the ID
+     */
+    String getStringId();
+
+    /**
+     * Gets the datapathId of the switch.
+     *
+     * @return the switch dpid in long format
+     */
+    long getId();
+
+    /**
+     * fetch the manufacturer description.
+     * @return the description
+     */
+    String manufacturerDescription();
+
+    /**
+     * fetch the datapath description.
+     * @return the description
+     */
+    String datapathDescription();
+
+    /**
+     * fetch the hardware description.
+     * @return the description
+     */
+    String hardwareDescription();
+
+    /**
+     * fetch the software description.
+     * @return the description
+     */
+    String softwareDescription();
+
+    /**
+     * fetch the serial number.
+     * @return the serial
+     */
+    String serialNumber();
+
+    /**
+     * Checks if the switch is still connected.
+     *
+     * @return whether the switch is still connected
+     */
+    boolean isConnected();
+
+    /**
+     * Disconnects the switch by closing the TCP connection. Results in a call
+     * to the channel handler's channelDisconnected method for cleanup
+     */
+    void disconnectSwitch();
+
+    /**
+     * Notifies the controller that the device has responded to a set-role request.
+     *
+     * @param requested the role requested by the controller
+     * @param response the role set at the device
+     */
+    void returnRoleReply(RoleState requested, RoleState response);
+
+    /**
+     * Returns the switch device type.
+     *
+     * @return device type
+     */
+    Device.Type deviceType();
+
+    /**
+     * Identifies the channel used to communicate with the switch.
+     *
+     * @return string representation of the connection to the device
+     */
+    String channelId();
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitchListener.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitchListener.java
new file mode 100644
index 0000000..2da4133
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitchListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+
+/**
+ * Allows for providers interested in Switch events to be notified.
+ */
+public interface OpenFlowSwitchListener {
+
+    /**
+     * Notify that the switch was added.
+     * @param dpid the switch where the event occurred
+     */
+    void switchAdded(Dpid dpid);
+
+    /**
+     * Notify that the switch was removed.
+     * @param dpid the switch where the event occurred.
+     */
+    void switchRemoved(Dpid dpid);
+
+    /**
+     * Notify that the switch has changed in some way.
+     * @param dpid the switch that changed
+     */
+    void switchChanged(Dpid dpid);
+
+    /**
+     * Notify that a port has changed.
+     * @param dpid the switch on which the change happened.
+     * @param status the new state of the port.
+     */
+    void portChanged(Dpid dpid, OFPortStatus status);
+
+    /**
+     * Notify that a role imposed on a switch failed to take hold.
+     *
+     * @param dpid the switch that failed role assertion
+     * @param requested the role controller requested
+     * @param response role reply from the switch
+     */
+    void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response);
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/PacketListener.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/PacketListener.java
new file mode 100644
index 0000000..817a6cd
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/PacketListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+/**
+ * Notifies providers about Packet in events.
+ */
+public interface PacketListener {
+
+    /**
+     * Handles the packet.
+     *
+     * @param pktCtx the packet context
+     */
+    void handlePacket(OpenFlowPacketContext pktCtx);
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/PortDescPropertyType.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/PortDescPropertyType.java
new file mode 100644
index 0000000..3a0f1a0
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/PortDescPropertyType.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+/**
+ * Port description property types (OFPPDPT enums) in OF 1.3 &lt;.
+ */
+public enum PortDescPropertyType {
+    ETHERNET(0),            /* Ethernet port */
+    OPTICAL(1),             /* Optical port */
+    OPTICAL_TRANSPORT(2),   /* OF1.3 Optical transport extension */
+    PIPELINE_INPUT(2),      /* Ingress pipeline */
+    PIPELINE_OUTPUT(3),     /* Egress pipeline */
+    RECIRCULATE(4),         /* Recirculation */
+    EXPERIMENTER(0xffff);   /* Experimenter-implemented */
+
+    private final int value;
+
+    PortDescPropertyType(int v) {
+        value = v;
+    }
+
+    public int valueOf() {
+        return value;
+    }
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/RoleState.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/RoleState.java
new file mode 100644
index 0000000..b8304f3
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/RoleState.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.projectfloodlight.openflow.protocol.OFControllerRole;
+
+/**
+ * The role of the controller as it pertains to a particular switch.
+ * Note that this definition of the role enum is different from the
+ * OF1.3 definition. It is maintained here to be backward compatible to
+ * earlier versions of the controller code. This enum is translated
+ * to the OF1.3 enum, before role messages are sent to the switch.
+ * See sendRoleRequestMessage method in OFSwitchImpl
+ */
+public enum RoleState {
+    EQUAL(OFControllerRole.ROLE_EQUAL),
+    MASTER(OFControllerRole.ROLE_MASTER),
+    SLAVE(OFControllerRole.ROLE_SLAVE);
+
+    private RoleState(OFControllerRole nxRole) {
+        nxRole.ordinal();
+    }
+
+}
+
+
+
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ThirdPartyMessage.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ThirdPartyMessage.java
new file mode 100644
index 0000000..59ef33c
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/ThirdPartyMessage.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+import com.google.common.hash.PrimitiveSink;
+/**
+ * Used to support for the third party privacy flow rule.
+ * it implements OFMessage interface to use exist adapter API.
+ */
+public class ThirdPartyMessage implements OFMessage {
+
+    private final byte[] payLoad; //privacy flow rule
+
+    public ThirdPartyMessage(byte[] payLoad) {
+        this.payLoad = payLoad;
+    }
+
+    public byte[] payLoad() {
+        return payLoad;
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+     // Do nothing here for now.
+    }
+
+    @Override
+    public OFVersion getVersion() {
+     // Do nothing here for now.
+        return null;
+    }
+
+    @Override
+    public OFType getType() {
+     // Do nothing here for now.
+        return null;
+    }
+
+    @Override
+    public long getXid() {
+     // Do nothing here for now.
+        return 0;
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer channelBuffer) {
+     // Do nothing here for now.
+    }
+
+    @Override
+    public Builder createBuilder() {
+     // Do nothing here for now.
+        return null;
+    }
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/WithTypedPorts.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/WithTypedPorts.java
new file mode 100644
index 0000000..8b82b4a
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/WithTypedPorts.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import java.util.List;
+import java.util.Set;
+
+import org.projectfloodlight.openflow.protocol.OFObject;
+
+/**
+ * An interface implemented by OpenFlow devices that enables providers to
+ * retrieve ports based on port property.
+ */
+public interface WithTypedPorts {
+
+    /**
+     * Return a list of interfaces (ports) of the type associated with this
+     * OpenFlow switch.
+     *
+     * @param type The port description property type of requested ports
+     * @return A potentially empty list of ports.
+     */
+    List<? extends OFObject> getPortsOf(PortDescPropertyType type);
+
+    /**
+     * Returns the port property types supported by the driver implementing this
+     * interface.
+     *
+     * @return A set of port property types
+     */
+    Set<PortDescPropertyType> getPortTypes();
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
new file mode 100644
index 0000000..c717419
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.driver;
+
+import com.google.common.collect.Lists;
+import org.jboss.netty.channel.Channel;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.Device;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFExperimenter;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleRequest;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFRoleReply;
+import org.projectfloodlight.openflow.protocol.OFRoleRequest;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+/**
+ * An abstract representation of an OpenFlow switch. Can be extended by others
+ * to serve as a base for their vendor specific representation of a switch.
+ */
+public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
+        implements OpenFlowSwitchDriver {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private Channel channel;
+    protected String channelId;
+
+    private boolean connected;
+    protected boolean startDriverHandshakeCalled = false;
+    private Dpid dpid;
+    private OpenFlowAgent agent;
+    private final AtomicInteger xidCounter = new AtomicInteger(0);
+
+    private OFVersion ofVersion;
+
+    protected List<OFPortDescStatsReply> ports = new ArrayList<>();
+
+    protected boolean tableFull;
+
+    private RoleHandler roleMan;
+
+    // TODO this is accessed from multiple threads, but volatile may have performance implications
+    protected volatile RoleState role;
+
+    protected OFFeaturesReply features;
+    protected OFDescStatsReply desc;
+
+    private final AtomicReference<List<OFMessage>> messagesPendingMastership
+            = new AtomicReference<>();
+
+    @Override
+    public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
+        this.dpid = dpid;
+        this.desc = desc;
+        this.ofVersion = ofv;
+    }
+
+    //************************
+    // Channel related
+    //************************
+
+    @Override
+    public final void disconnectSwitch() {
+        this.channel.close();
+    }
+
+    @Override
+    public void sendMsg(OFMessage msg) {
+        this.sendMsg(Collections.singletonList(msg));
+    }
+
+    @Override
+    public final void sendMsg(List<OFMessage> msgs) {
+        /*
+           It is possible that in this block, we transition to SLAVE/EQUAL.
+           If this is the case, the supplied messages will race with the
+           RoleRequest message, and they could be rejected by the switch.
+           In the interest of performance, we will not protect this block with
+           a synchronization primitive, because the message would have just been
+           dropped anyway.
+        */
+        if (role == RoleState.MASTER) {
+            // fast path send when we are master
+
+            sendMsgsOnChannel(msgs);
+            return;
+        }
+        // check to see if mastership transition is in progress
+        synchronized (messagesPendingMastership) {
+            /*
+               messagesPendingMastership is used as synchronization variable for
+               all mastership related changes. In this block, mastership (including
+               role update) will have either occurred or not.
+            */
+            if (role == RoleState.MASTER) {
+                // transition to MASTER complete, send messages
+                sendMsgsOnChannel(msgs);
+                return;
+            }
+
+            List<OFMessage> messages = messagesPendingMastership.get();
+            if (messages != null) {
+                // we are transitioning to MASTER, so add messages to queue
+                messages.addAll(msgs);
+                log.debug("Enqueue message for switch {}. queue size after is {}",
+                          dpid, messages.size());
+            } else {
+                // not transitioning to MASTER
+                log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
+                         dpid, role, channel.isConnected(), msgs);
+            }
+        }
+    }
+
+    private void sendMsgsOnChannel(List<OFMessage> msgs) {
+        if (channel.isConnected()) {
+            channel.write(msgs);
+        } else {
+            log.warn("Dropping messages for switch {} because channel is not connected: {}",
+                     dpid, msgs);
+        }
+    }
+
+    @Override
+    public final void sendRoleRequest(OFMessage msg) {
+        if (msg instanceof OFRoleRequest ||
+                msg instanceof OFNiciraControllerRoleRequest) {
+            sendMsgsOnChannel(Collections.singletonList(msg));
+            return;
+        }
+        throw new IllegalArgumentException("Someone is trying to send " +
+                                                   "a non role request message");
+    }
+
+    @Override
+    public final void sendHandshakeMessage(OFMessage message) {
+        if (!this.isDriverHandshakeComplete()) {
+            sendMsgsOnChannel(Collections.singletonList(message));
+        }
+    }
+
+    @Override
+    public final boolean isConnected() {
+        return this.connected;
+    }
+
+    @Override
+    public final void setConnected(boolean connected) {
+        this.connected = connected;
+    }
+
+    @Override
+    public final void setChannel(Channel channel) {
+        this.channel = channel;
+        final SocketAddress address = channel.getRemoteAddress();
+        if (address instanceof InetSocketAddress) {
+            final InetSocketAddress inetAddress = (InetSocketAddress) address;
+            final IpAddress ipAddress = IpAddress.valueOf(inetAddress.getAddress());
+            if (ipAddress.isIp4()) {
+                channelId = ipAddress.toString() + ':' + inetAddress.getPort();
+            } else {
+                channelId = '[' + ipAddress.toString() + "]:" + inetAddress.getPort();
+            }
+        }
+    }
+
+    @Override
+    public String channelId() {
+        return channelId;
+    }
+
+    //************************
+    // Switch features related
+    //************************
+
+    @Override
+    public final long getId() {
+        return this.dpid.value();
+    }
+
+    @Override
+    public final String getStringId() {
+        return this.dpid.toString();
+    }
+
+    @Override
+    public final void setOFVersion(OFVersion ofV) {
+        this.ofVersion = ofV;
+    }
+
+    @Override
+    public void setTableFull(boolean full) {
+        this.tableFull = full;
+    }
+
+    @Override
+    public void setFeaturesReply(OFFeaturesReply featuresReply) {
+        this.features = featuresReply;
+    }
+
+    @Override
+    public abstract Boolean supportNxRole();
+
+    //************************
+    //  Message handling
+    //************************
+    /**
+     * Handle the message coming from the dataplane.
+     *
+     * @param m the actual message
+     */
+    @Override
+    public final void handleMessage(OFMessage m) {
+        if (this.role == RoleState.MASTER || m instanceof OFPortStatus) {
+            this.agent.processMessage(dpid, m);
+        }
+    }
+
+    @Override
+    public RoleState getRole() {
+        return role;
+    }
+
+    @Override
+    public final boolean connectSwitch() {
+        return this.agent.addConnectedSwitch(dpid, this);
+    }
+
+    @Override
+    public final boolean activateMasterSwitch() {
+        return this.agent.addActivatedMasterSwitch(dpid, this);
+    }
+
+    @Override
+    public final boolean activateEqualSwitch() {
+        return this.agent.addActivatedEqualSwitch(dpid, this);
+    }
+
+    @Override
+    public final void transitionToEqualSwitch() {
+        this.agent.transitionToEqualSwitch(dpid);
+    }
+
+    @Override
+    public final void transitionToMasterSwitch() {
+        this.agent.transitionToMasterSwitch(dpid);
+        synchronized (messagesPendingMastership) {
+            List<OFMessage> messages = messagesPendingMastership.get();
+            if (messages != null) {
+                this.sendMsg(messages);
+                log.debug("Sending {} pending messages to switch {}",
+                          messages.size(), dpid);
+                messagesPendingMastership.set(null);
+            }
+            // perform role transition after clearing messages queue
+            this.role = RoleState.MASTER;
+        }
+    }
+
+    @Override
+    public final void removeConnectedSwitch() {
+        this.agent.removeConnectedSwitch(dpid);
+    }
+
+    @Override
+    public OFFactory factory() {
+        return OFFactories.getFactory(ofVersion);
+    }
+
+    @Override
+    public void setPortDescReply(OFPortDescStatsReply portDescReply) {
+        this.ports.add(portDescReply);
+    }
+
+    @Override
+    public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
+        this.ports.addAll(portDescReplies);
+    }
+
+    @Override
+    public void returnRoleReply(RoleState requested, RoleState response) {
+        this.agent.returnRoleReply(dpid, requested, response);
+    }
+
+    @Override
+    public abstract void startDriverHandshake();
+
+    @Override
+    public abstract boolean isDriverHandshakeComplete();
+
+    @Override
+    public abstract void processDriverHandshakeMessage(OFMessage m);
+
+
+    // Role Handling
+
+    @Override
+    public void setRole(RoleState role) {
+        try {
+            if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
+                // perform role transition to SLAVE/EQUAL before sending role request
+                this.role = role;
+            }
+            if (this.roleMan.sendRoleRequest(role, RoleRecvStatus.MATCHED_SET_ROLE)) {
+                log.debug("Sending role {} to switch {}", role, getStringId());
+                if (role == RoleState.MASTER) {
+                    synchronized (messagesPendingMastership) {
+                        if (messagesPendingMastership.get() == null) {
+                            log.debug("Initializing new message queue for switch {}", dpid);
+                            /*
+                               The presence of messagesPendingMastership indicates that
+                               a switch is currently transitioning to MASTER, but
+                               is still awaiting role reply from switch.
+                            */
+                            messagesPendingMastership.set(Lists.newArrayList());
+                        }
+                    }
+                }
+            } else if (role == RoleState.MASTER) {
+                // role request not support; transition switch to MASTER
+                this.role = role;
+            }
+        } catch (IOException e) {
+            log.error("Unable to write to switch {}.", this.dpid);
+        }
+    }
+
+    @Override
+    public void reassertRole() {
+        // TODO should messages be sent directly or queue during reassertion?
+        if (this.getRole() == RoleState.MASTER) {
+            log.warn("Received permission error from switch {} while " +
+                    "being master. Reasserting master role.",
+                    this.getStringId());
+            this.setRole(RoleState.MASTER);
+        }
+    }
+
+    @Override
+    public void handleRole(OFMessage m) throws SwitchStateException {
+        RoleReplyInfo rri = roleMan.extractOFRoleReply((OFRoleReply) m);
+        RoleRecvStatus rrs = roleMan.deliverRoleReply(rri);
+        if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
+            if (rri.getRole() == RoleState.MASTER) {
+                this.transitionToMasterSwitch();
+            } else if (rri.getRole() == RoleState.EQUAL ||
+                       rri.getRole() == RoleState.SLAVE) {
+                this.transitionToEqualSwitch();
+            }
+        }  else {
+            log.warn("Failed to set role for {}", this.getStringId());
+        }
+    }
+
+    @Override
+    public void handleNiciraRole(OFMessage m) throws SwitchStateException {
+        RoleState r = this.roleMan.extractNiciraRoleReply((OFExperimenter) m);
+        if (r == null) {
+            // The message wasn't really a Nicira role reply. We just
+            // dispatch it to the OFMessage listeners in this case.
+            this.handleMessage(m);
+            return;
+        }
+
+        RoleRecvStatus rrs = this.roleMan.deliverRoleReply(
+                new RoleReplyInfo(r, null, m.getXid()));
+        if (rrs == RoleRecvStatus.MATCHED_SET_ROLE) {
+            if (r == RoleState.MASTER) {
+                this.transitionToMasterSwitch();
+            } else if (r == RoleState.EQUAL ||
+                       r == RoleState.SLAVE) {
+                this.transitionToEqualSwitch();
+            }
+        } else {
+            this.disconnectSwitch();
+        }
+    }
+
+    @Override
+    public boolean handleRoleError(OFErrorMsg error) {
+        try {
+            return RoleRecvStatus.OTHER_EXPECTATION != this.roleMan.deliverError(error);
+        } catch (SwitchStateException e) {
+            this.disconnectSwitch();
+        }
+        return true;
+    }
+
+    @Override
+    public final void setAgent(OpenFlowAgent ag) {
+        if (this.agent == null) {
+            this.agent = ag;
+        }
+    }
+
+    @Override
+    public final void setRoleHandler(RoleHandler roleHandler) {
+        if (this.roleMan == null) {
+            this.roleMan = roleHandler;
+        }
+    }
+
+    @Override
+    public void setSwitchDescription(OFDescStatsReply d) {
+        this.desc = d;
+    }
+
+    @Override
+    public int getNextTransactionId() {
+        return this.xidCounter.getAndIncrement();
+    }
+
+    @Override
+    public List<OFPortDesc> getPorts() {
+        return this.ports.stream()
+                  .flatMap(portReply -> portReply.getEntries().stream())
+                  .collect(Collectors.toList());
+    }
+
+    @Override
+    public String manufacturerDescription() {
+        return this.desc.getMfrDesc();
+    }
+
+    @Override
+    public String datapathDescription() {
+        return this.desc.getDpDesc();
+    }
+
+    @Override
+    public String hardwareDescription() {
+        return this.desc.getHwDesc();
+    }
+
+    @Override
+    public String softwareDescription() {
+        return this.desc.getSwDesc();
+    }
+
+    @Override
+    public String serialNumber() {
+        return this.desc.getSerialNum();
+    }
+
+    @Override
+    public Device.Type deviceType() {
+        return Device.Type.SWITCH;
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName() + " [" + ((channel != null)
+                ? channel.getRemoteAddress() : "?")
+                + " DPID[" + ((getStringId() != null) ? getStringId() : "?") + "]]";
+    }
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowAgent.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowAgent.java
new file mode 100644
index 0000000..ad6dede
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowAgent.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+
+/**
+ * Responsible for keeping track of the current set of switches
+ * connected to the system. As well as whether they are in Master
+ * role or not.
+ *
+ */
+public interface OpenFlowAgent {
+
+    /**
+     * Add a switch that has just connected to the system.
+     * @param dpid the dpid to add
+     * @param sw the actual switch object.
+     * @return true if added, false otherwise.
+     */
+    boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw);
+
+    /**
+     * Checks if the activation for this switch is valid.
+     * @param dpid the dpid to check
+     * @return true if valid, false otherwise
+     */
+    boolean validActivation(Dpid dpid);
+
+    /**
+     * Called when a switch is activated, with this controller's role as MASTER.
+     * @param dpid the dpid to add.
+     * @param sw the actual switch
+     * @return true if added, false otherwise.
+     */
+    boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw);
+
+    /**
+     * Called when a switch is activated, with this controller's role as EQUAL.
+     * @param dpid the dpid to add.
+     * @param sw the actual switch
+     * @return true if added, false otherwise.
+     */
+    boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw);
+
+    /**
+     * Called when this controller's role for a switch transitions from equal
+     * to master. For 1.0 switches, we internally refer to the role 'slave' as
+     * 'equal' - so this transition is equivalent to 'addActivatedMasterSwitch'.
+     * @param dpid the dpid to transistion.
+     */
+    void transitionToMasterSwitch(Dpid dpid);
+
+    /**
+     * Called when this controller's role for a switch transitions to equal.
+     * For 1.0 switches, we internally refer to the role 'slave' as
+     * 'equal'.
+     * @param dpid the dpid to transistion.
+     */
+    void transitionToEqualSwitch(Dpid dpid);
+
+    /**
+     * Clear all state in controller switch maps for a switch that has
+     * disconnected from the local controller. Also release control for
+     * that switch from the global repository. Notify switch listeners.
+     * @param dpid the dpid to remove.
+     */
+    void removeConnectedSwitch(Dpid dpid);
+
+    /**
+     * Process a message coming from a switch.
+     *
+     * @param dpid the dpid the message came on.
+     * @param m the message to process
+     */
+    void processMessage(Dpid dpid, OFMessage m);
+
+    /**
+     * Notifies the controller that role assertion has failed.
+     *
+     * @param dpid the switch that failed role assertion
+     * @param requested the role controller requested
+     * @param response role reply from the switch
+     */
+    void returnRoleReply(Dpid dpid, RoleState requested, RoleState response);
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriver.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriver.java
new file mode 100644
index 0000000..b259388
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriver.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+import org.jboss.netty.channel.Channel;
+import org.onosproject.net.driver.HandlerBehaviour;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+import java.util.List;
+
+/**
+ * Represents the driver side of an OpenFlow switch.
+ * This interface should never be exposed to consumers.
+ *
+ */
+public interface OpenFlowSwitchDriver extends OpenFlowSwitch, HandlerBehaviour {
+
+    /**
+     * Sets the OpenFlow agent to be used. This method
+     * can only be called once.
+     * @param agent the agent to set.
+     */
+    void setAgent(OpenFlowAgent agent);
+
+    /**
+     * Sets the Role handler object.
+     * This method can only be called once.
+     * @param roleHandler the roleHandler class
+     */
+    void setRoleHandler(RoleHandler roleHandler);
+
+    /**
+     * Reasserts this controllers role to the switch.
+     * Useful in cases where the switch no longer agrees
+     * that this controller has the role it claims.
+     */
+    void reassertRole();
+
+    /**
+     * Handle the situation where the role request triggers an error.
+     * @param error the error to handle.
+     * @return true if handled, false if not.
+     */
+    boolean handleRoleError(OFErrorMsg error);
+
+    /**
+     * If this driver know of Nicira style role messages, these should
+     * be handled here.
+     * @param m the role message to handle.
+     * @throws SwitchStateException if the message received was
+     *  not a nicira role or was malformed.
+     */
+    void handleNiciraRole(OFMessage m) throws SwitchStateException;
+
+    /**
+     * Handle OF 1.x (where x &gt; 0) role messages.
+     * @param m the role message to handle
+     * @throws SwitchStateException if the message received was
+     *  not a nicira role or was malformed.
+     */
+    void handleRole(OFMessage m) throws SwitchStateException;
+
+    /**
+     * Announce to the OpenFlow agent that this switch has connected.
+     * @return true if successful, false if duplicate switch.
+     */
+    boolean connectSwitch();
+
+    /**
+     * Activate this MASTER switch-controller relationship in the OF agent.
+     * @return true is successful, false is switch has not
+     * connected or is unknown to the system.
+     */
+    boolean activateMasterSwitch();
+
+    /**
+     * Activate this EQUAL switch-controller relationship in the OF agent.
+     * @return true is successful, false is switch has not
+     * connected or is unknown to the system.
+     */
+    boolean activateEqualSwitch();
+
+    /**
+     * Transition this switch-controller relationship to an EQUAL state.
+     */
+    void transitionToEqualSwitch();
+
+    /**
+     * Transition this switch-controller relationship to an Master state.
+     */
+    void transitionToMasterSwitch();
+
+    /**
+     * Remove this switch from the openflow agent.
+     */
+    void removeConnectedSwitch();
+
+    /**
+     * Sets the ports on this switch.
+     * @param portDescReply the port set and descriptions
+     */
+    void setPortDescReply(OFPortDescStatsReply portDescReply);
+
+    /**
+     * Sets the ports on this switch.
+     * @param portDescReplies list of port set and descriptions
+     */
+    void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies);
+
+    /**
+     * Sets the features reply for this switch.
+     * @param featuresReply the features to set.
+     */
+    void setFeaturesReply(OFFeaturesReply featuresReply);
+
+    /**
+     * Sets the switch description.
+     * @param desc the descriptions
+     */
+    void setSwitchDescription(OFDescStatsReply desc);
+
+    /**
+     * Gets the next transaction id to use.
+     * @return the xid
+     */
+    int getNextTransactionId();
+
+
+    /**
+     * Sets the OF version for this switch.
+     * @param ofV the version to set.
+     */
+    void setOFVersion(OFVersion ofV);
+
+    /**
+     * Sets this switch has having a full flowtable.
+     * @param full true if full, false otherswise.
+     */
+    void setTableFull(boolean full);
+
+    /**
+     * Sets the associated Netty channel for this switch.
+     * @param channel the Netty channel
+     */
+    void setChannel(Channel channel);
+
+    /**
+     * Sets whether the switch is connected.
+     *
+     * @param connected whether the switch is connected
+     */
+    void setConnected(boolean connected);
+
+    /**
+     * Initialises the behaviour.
+     * @param dpid a dpid
+     * @param desc a switch description
+     * @param ofv OpenFlow version
+     */
+    void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv);
+
+    /**
+     * Does this switch support Nicira Role messages.
+     * @return true if supports, false otherwise.
+     */
+    Boolean supportNxRole();
+
+
+    /**
+     * Starts the driver specific handshake process.
+     */
+    void startDriverHandshake();
+
+    /**
+     * Checks whether the driver specific handshake is complete.
+     * @return true is finished, false if not.
+     */
+    boolean isDriverHandshakeComplete();
+
+    /**
+     * Process a message during the driver specific handshake.
+     * @param m the message to process.
+     */
+    void processDriverHandshakeMessage(OFMessage m);
+
+    /**
+     * Sends only role request messages.
+     *
+     * @param message a role request message.
+     */
+    void sendRoleRequest(OFMessage message);
+
+    /**
+     * Allows the handshaker behaviour to send messages during the
+     * handshake phase only.
+     *
+     * @param message an OpenFlow message
+     */
+    void sendHandshakeMessage(OFMessage message);
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriverFactory.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriverFactory.java
new file mode 100644
index 0000000..a0d8f18
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/OpenFlowSwitchDriverFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+import org.onosproject.openflow.controller.Dpid;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+/**
+ * Switch factory which returns concrete switch objects for the
+ * physical openflow switch in use.
+ *
+ */
+public interface OpenFlowSwitchDriverFactory {
+
+
+    /**
+     * Constructs the real openflow switch representation.
+     * @param dpid the dpid for this switch.
+     * @param desc its description.
+     * @param ofv the OF version in use
+     * @return the openflow switch representation.
+     */
+    OpenFlowSwitchDriver getOFSwitchImpl(Dpid dpid,
+            OFDescStatsReply desc, OFVersion ofv);
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleHandler.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleHandler.java
new file mode 100644
index 0000000..b406888
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleHandler.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+import java.io.IOException;
+
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFExperimenter;
+import org.projectfloodlight.openflow.protocol.OFRoleReply;
+
+/**
+ * Role handling.
+ *
+ */
+public interface RoleHandler {
+
+    /**
+     * Extract the role from an OFVendor message.
+     *
+     * Extract the role from an OFVendor message if the message is a
+     * Nicira role reply. Otherwise return null.
+     *
+     * @param experimenterMsg The vendor message to parse.
+     * @return The role in the message if the message is a Nicira role
+     * reply, null otherwise.
+     * @throws SwitchStateException If the message is a Nicira role reply
+     * but the numeric role value is unknown.
+     */
+    RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
+            throws SwitchStateException;
+
+    /**
+     * Send a role request with the given role to the switch and update
+     * the pending request and timestamp.
+     * Sends an OFPT_ROLE_REQUEST to an OF1.3 switch, OR
+     * Sends an NX_ROLE_REQUEST to an OF1.0 switch if configured to support it
+     * in the IOFSwitch driver. If not supported, this method sends nothing
+     * and returns 'false'. The caller should take appropriate action.
+     *
+     * One other optimization we do here is that for OF1.0 switches with
+     * Nicira role message support, we force the Role.EQUAL to become
+     * Role.SLAVE, as there is no defined behavior for the Nicira role OTHER.
+     * We cannot expect it to behave like SLAVE. We don't have this problem with
+     * OF1.3 switches, because Role.EQUAL is well defined and we can simulate
+     * SLAVE behavior by using ASYNC messages.
+     *
+     * @param role role to request
+     * @param exp expectation
+     * @throws IOException when I/O exception of some sort has occurred
+     * @return false if and only if the switch does not support role-request
+     * messages, according to the switch driver; true otherwise.
+     */
+    boolean sendRoleRequest(RoleState role, RoleRecvStatus exp)
+            throws IOException;
+
+    /**
+     * Extract the role information from an OF1.3 Role Reply Message.
+     * @param rrmsg role reply message
+     * @return RoleReplyInfo object
+     * @throws SwitchStateException If unknown role encountered
+     */
+    RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
+            throws SwitchStateException;
+
+    /**
+     * Deliver a received role reply.
+     *
+     * Check if a request is pending and if the received reply matches the
+     * the expected pending reply (we check both role and xid) we set
+     * the role for the switch/channel.
+     *
+     * If a request is pending but doesn't match the reply we ignore it, and
+     * return
+     *
+     * If no request is pending we disconnect with a SwitchStateException
+     *
+     * @param rri information about role-reply in format that
+     *                      controller can understand.
+     * @return result comparing expected and received reply
+     * @throws SwitchStateException if no request is pending
+     */
+    RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
+            throws SwitchStateException;
+
+
+    /**
+     * Called if we receive an  error message. If the xid matches the
+     * pending request we handle it otherwise we ignore it.
+     *
+     * Note: since we only keep the last pending request we might get
+     * error messages for earlier role requests that we won't be able
+     * to handle
+     * @param error error message
+     * @return result comparing expected and received reply
+     * @throws SwitchStateException if switch did not support requested role
+     */
+    RoleRecvStatus deliverError(OFErrorMsg error)
+            throws SwitchStateException;
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleRecvStatus.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleRecvStatus.java
new file mode 100644
index 0000000..88c4cc7
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleRecvStatus.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+/**
+ * When we remove a pending role request we use this enum to indicate how we
+ * arrived at the decision. When we send a role request to the switch, we
+ * also use  this enum to indicate what we expect back from the switch, so the
+ * role changer can match the reply to our expectation.
+ */
+public enum RoleRecvStatus {
+    /** The switch returned an error indicating that roles are not.
+     * supported*/
+    UNSUPPORTED,
+    /** The request timed out. */
+    NO_REPLY,
+    /** The reply was old, there is a newer request pending. */
+    OLD_REPLY,
+    /**
+     *  The reply's role matched the role that this controller set in the
+     *  request message - invoked either initially at startup or to reassert
+     *  current role.
+     */
+    MATCHED_CURRENT_ROLE,
+    /**
+     *  The reply's role matched the role that this controller set in the
+     *  request message - this is the result of a callback from the
+     *  global registry, followed by a role request sent to the switch.
+     */
+    MATCHED_SET_ROLE,
+    /**
+     * The reply's role was a response to the query made by this controller.
+     */
+    REPLY_QUERY,
+    /** We received a role reply message from the switch
+     *  but the expectation was unclear, or there was no expectation.
+     */
+    OTHER_EXPECTATION,
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleReplyInfo.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleReplyInfo.java
new file mode 100644
index 0000000..dc9b6ba
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/RoleReplyInfo.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.types.U64;
+
+/**
+ * Helper class returns role reply information in the format understood
+ * by the controller.
+ */
+public class RoleReplyInfo {
+    private final RoleState role;
+    private final U64 genId;
+    private final long xid;
+
+    public RoleReplyInfo(RoleState role, U64 genId, long xid) {
+        this.role = role;
+        this.genId = genId;
+        this.xid = xid;
+    }
+    public RoleState getRole() {
+        return role;
+    }
+    public U64 getGenId() {
+        return genId;
+    }
+    public long getXid() {
+        return xid;
+    }
+    @Override
+    public String toString() {
+        return "[Role:" + role + " GenId:" + genId + " Xid:" + xid + "]";
+    }
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeAlreadyStarted.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeAlreadyStarted.java
new file mode 100644
index 0000000..96b4bd7
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeAlreadyStarted.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+/**
+ * Thrown when IOFSwitch.startDriverHandshake() is called more than once.
+ *
+ */
+public class SwitchDriverSubHandshakeAlreadyStarted extends
+    SwitchDriverSubHandshakeException {
+    private static final long serialVersionUID = -5491845708752443501L;
+
+    public SwitchDriverSubHandshakeAlreadyStarted() {
+        super();
+    }
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeCompleted.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeCompleted.java
new file mode 100644
index 0000000..b0f59fe
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeCompleted.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+import org.projectfloodlight.openflow.protocol.OFMessage;
+
+
+/**
+ * Indicates that a message was passed to a switch driver's subhandshake
+ * handling code but the driver has already completed the sub-handshake.
+ *
+ */
+public class SwitchDriverSubHandshakeCompleted
+        extends SwitchDriverSubHandshakeException {
+    private static final long serialVersionUID = -8817822245846375995L;
+
+    public SwitchDriverSubHandshakeCompleted(OFMessage m) {
+        super("Sub-Handshake is already complete but received message "
+              + m.getType());
+    }
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeException.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeException.java
new file mode 100644
index 0000000..1bc750a
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+/**
+ * Base class for exception thrown by switch driver sub-handshake processing.
+ *
+ */
+public class SwitchDriverSubHandshakeException extends RuntimeException {
+    private static final long serialVersionUID = -6257836781419604438L;
+
+    protected SwitchDriverSubHandshakeException() {
+        super();
+    }
+
+    protected SwitchDriverSubHandshakeException(String arg0, Throwable arg1) {
+        super(arg0, arg1);
+    }
+
+    protected SwitchDriverSubHandshakeException(String arg0) {
+        super(arg0);
+    }
+
+    protected SwitchDriverSubHandshakeException(Throwable arg0) {
+        super(arg0);
+    }
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeNotStarted.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeNotStarted.java
new file mode 100644
index 0000000..a073683
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeNotStarted.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+/**
+ * Thrown when a switch driver's sub-handshake has not been started but an
+ * operation requiring the sub-handshake has been attempted.
+ *
+ */
+public class SwitchDriverSubHandshakeNotStarted extends
+    SwitchDriverSubHandshakeException {
+    private static final long serialVersionUID = -5491845708752443501L;
+
+    public SwitchDriverSubHandshakeNotStarted() {
+        super();
+    }
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeStateException.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeStateException.java
new file mode 100644
index 0000000..3f4be81
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchDriverSubHandshakeStateException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.driver;
+
+/**
+ * Thrown when a switch driver's sub-handshake state-machine receives an
+ * unexpected OFMessage and/or is in an invald state.
+ *
+ */
+public class SwitchDriverSubHandshakeStateException extends
+    SwitchDriverSubHandshakeException {
+    private static final long serialVersionUID = -8249926069195147051L;
+
+    public SwitchDriverSubHandshakeStateException(String msg) {
+        super(msg);
+    }
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchStateException.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchStateException.java
new file mode 100644
index 0000000..30c4e91
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/SwitchStateException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.driver;
+
+/**
+ * This exception indicates an error or unexpected message during
+ * message handling. E.g., if an OFMessage is received that is illegal or
+ * unexpected given the current handshake state.
+ *
+ * We don't allow wrapping other exception in a switch state exception. We
+ * only log the SwitchStateExceptions message so the causing exceptions
+ * stack trace is generally not available.
+ *
+ */
+public class SwitchStateException extends Exception {
+
+    private static final long serialVersionUID = 9153954512470002631L;
+
+    public SwitchStateException() {
+        super();
+    }
+
+    public SwitchStateException(String arg0, Throwable arg1) {
+        super(arg0, arg1);
+    }
+
+    public SwitchStateException(String arg0) {
+        super(arg0);
+    }
+
+    public SwitchStateException(Throwable arg0) {
+        super(arg0);
+    }
+
+}
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/package-info.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/package-info.java
new file mode 100644
index 0000000..c03a584
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * OpenFlow controller switch driver API.
+ */
+package org.onosproject.openflow.controller.driver;
diff --git a/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/package-info.java b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/package-info.java
new file mode 100644
index 0000000..5477842
--- /dev/null
+++ b/protocols/openflow/api/src/main/java/org/onosproject/openflow/controller/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * OpenFlow controller API.
+ */
+package org.onosproject.openflow.controller;
diff --git a/protocols/openflow/api/src/test/java/org/onosproject/openflow/controller/OpenflowControllerAdapter.java b/protocols/openflow/api/src/test/java/org/onosproject/openflow/controller/OpenflowControllerAdapter.java
new file mode 100644
index 0000000..f4fe490
--- /dev/null
+++ b/protocols/openflow/api/src/test/java/org/onosproject/openflow/controller/OpenflowControllerAdapter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller;
+
+import org.projectfloodlight.openflow.protocol.OFMessage;
+
+/**
+ * Test adapter for the OpenFlow controller interface.
+ */
+public class OpenflowControllerAdapter implements OpenFlowController {
+    @Override
+    public Iterable<OpenFlowSwitch> getSwitches() {
+        return null;
+    }
+
+    @Override
+    public Iterable<OpenFlowSwitch> getMasterSwitches() {
+        return null;
+    }
+
+    @Override
+    public Iterable<OpenFlowSwitch> getEqualSwitches() {
+        return null;
+    }
+
+    @Override
+    public OpenFlowSwitch getSwitch(Dpid dpid) {
+        return null;
+    }
+
+    @Override
+    public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
+        return null;
+    }
+
+    @Override
+    public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
+        return null;
+    }
+
+    @Override
+    public void addListener(OpenFlowSwitchListener listener) {
+    }
+
+    @Override
+    public void removeListener(OpenFlowSwitchListener listener) {
+    }
+
+    @Override
+    public void addPacketListener(int priority, PacketListener listener) {
+    }
+
+    @Override
+    public void removePacketListener(PacketListener listener) {
+    }
+
+    @Override
+    public void write(Dpid dpid, OFMessage msg) {
+    }
+
+    @Override
+    public void processPacket(Dpid dpid, OFMessage msg) {
+    }
+
+    @Override
+    public void setRole(Dpid dpid, RoleState role) {
+    }
+
+    @Override
+    public void addEventListener(OpenFlowEventListener listener) {
+    }
+
+    @Override
+    public void removeEventListener(OpenFlowEventListener listener) {
+    }
+}
diff --git a/protocols/openflow/ctl/pom.xml b/protocols/openflow/ctl/pom.xml
new file mode 100644
index 0000000..56d4855
--- /dev/null
+++ b/protocols/openflow/ctl/pom.xml
@@ -0,0 +1,65 @@
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos-of</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-of-ctl</artifactId>
+    <packaging>bundle</packaging>
+
+    <description>ONOS OpenFlow controller subsystem API</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-of-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.onosproject</groupId>
+                <artifactId>onos-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
new file mode 100644
index 0000000..56b3a99
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.jboss.netty.channel.group.DefaultChannelGroup;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.driver.OpenFlowAgent;
+import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.FileInputStream;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.openflow.controller.Dpid.uri;
+
+
+/**
+ * The main controller class.  Handles all setup and network listeners
+ * - Distributed ownership control of switch through IControllerRegistryService
+ */
+public class Controller {
+
+    protected static final Logger log = LoggerFactory.getLogger(Controller.class);
+
+    protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13);
+    protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10);
+    private static final boolean TLS_DISABLED = false;
+    private static final short MIN_KS_LENGTH = 6;
+
+    protected HashMap<String, String> controllerNodeIPsCache;
+
+    private ChannelGroup cg;
+
+    // Configuration options
+    protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653);
+    protected int workerThreads = 16;
+
+    // Start time of the controller
+    protected long systemStartTime;
+
+    private OpenFlowAgent agent;
+
+    private NioServerSocketChannelFactory execFactory;
+
+    protected String ksLocation;
+    protected String tsLocation;
+    protected char[] ksPwd;
+    protected char[] tsPwd;
+    protected SSLEngine serverSslEngine;
+
+    // Perf. related configuration
+    protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024;
+    private DriverService driverService;
+    private boolean enableOfTls = TLS_DISABLED;
+
+    // ***************
+    // Getters/Setters
+    // ***************
+
+    public OFFactory getOFMessageFactory10() {
+        return FACTORY10;
+    }
+
+
+    public OFFactory getOFMessageFactory13() {
+        return FACTORY13;
+    }
+
+    // **************
+    // Initialization
+    // **************
+
+    /**
+     * Tell controller that we're ready to accept switches loop.
+     */
+    public void run() {
+
+        try {
+            final ServerBootstrap bootstrap = createServerBootStrap();
+
+            bootstrap.setOption("reuseAddr", true);
+            bootstrap.setOption("child.keepAlive", true);
+            bootstrap.setOption("child.tcpNoDelay", true);
+            bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE);
+
+            ChannelPipelineFactory pfact =
+                    new OpenflowPipelineFactory(this, null, serverSslEngine);
+            bootstrap.setPipelineFactory(pfact);
+            cg = new DefaultChannelGroup();
+            openFlowPorts.forEach(port -> {
+                InetSocketAddress sa = new InetSocketAddress(port);
+                cg.add(bootstrap.bind(sa));
+                log.info("Listening for switch connections on {}", sa);
+            });
+
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    private ServerBootstrap createServerBootStrap() {
+
+        if (workerThreads == 0) {
+            execFactory = new NioServerSocketChannelFactory(
+                    Executors.newCachedThreadPool(groupedThreads("onos/of", "boss-%d")),
+                    Executors.newCachedThreadPool(groupedThreads("onos/of", "worker-%d")));
+            return new ServerBootstrap(execFactory);
+        } else {
+            execFactory = new NioServerSocketChannelFactory(
+                    Executors.newCachedThreadPool(groupedThreads("onos/of", "boss-%d")),
+                    Executors.newCachedThreadPool(groupedThreads("onos/of", "worker-%d")), workerThreads);
+            return new ServerBootstrap(execFactory);
+        }
+    }
+
+    public void setConfigParams(Dictionary<?, ?> properties) {
+        String ports = get(properties, "openflowPorts");
+        if (!Strings.isNullOrEmpty(ports)) {
+            this.openFlowPorts = Stream.of(ports.split(","))
+                                       .map(s -> Integer.parseInt(s))
+                                       .collect(Collectors.toList());
+        }
+        log.debug("OpenFlow ports set to {}", this.openFlowPorts);
+
+        String threads = get(properties, "workerThreads");
+        if (!Strings.isNullOrEmpty(threads)) {
+            this.workerThreads = Integer.parseInt(threads);
+        }
+        log.debug("Number of worker threads set to {}", this.workerThreads);
+    }
+
+    /**
+     * Initialize internal data structures.
+     */
+    public void init() {
+        // These data structures are initialized here because other
+        // module's startUp() might be called before ours
+        this.controllerNodeIPsCache = new HashMap<>();
+
+        this.systemStartTime = System.currentTimeMillis();
+
+        try {
+            getTlsParameters();
+            if (enableOfTls) {
+                initSsl();
+            }
+        } catch (Exception ex) {
+            log.error("SSL init failed: {}", ex.getMessage());
+        }
+
+    }
+
+    private void getTlsParameters() {
+        String tempString = System.getProperty("enableOFTLS");
+        enableOfTls = Strings.isNullOrEmpty(tempString) ? TLS_DISABLED : Boolean.parseBoolean(tempString);
+        log.info("OpenFlow Security is {}", enableOfTls ? "enabled" : "disabled");
+        if (enableOfTls) {
+            ksLocation = System.getProperty("javax.net.ssl.keyStore");
+            if (Strings.isNullOrEmpty(ksLocation)) {
+                enableOfTls = TLS_DISABLED;
+                return;
+            }
+            tsLocation = System.getProperty("javax.net.ssl.trustStore");
+            if (Strings.isNullOrEmpty(tsLocation)) {
+                enableOfTls = TLS_DISABLED;
+                return;
+            }
+            ksPwd = System.getProperty("javax.net.ssl.keyStorePassword").toCharArray();
+            if (MIN_KS_LENGTH > ksPwd.length) {
+                enableOfTls = TLS_DISABLED;
+                return;
+            }
+            tsPwd = System.getProperty("javax.net.ssl.trustStorePassword").toCharArray();
+            if (MIN_KS_LENGTH > tsPwd.length) {
+                enableOfTls = TLS_DISABLED;
+                return;
+            }
+        }
+    }
+
+    private void initSsl() throws Exception {
+
+        TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        KeyStore ts = KeyStore.getInstance("JKS");
+        ts.load(new FileInputStream(tsLocation), tsPwd);
+        tmFactory.init(ts);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        KeyStore ks = KeyStore.getInstance("JKS");
+        ks.load(new FileInputStream(ksLocation), ksPwd);
+        kmf.init(ks, ksPwd);
+
+        SSLContext serverContext = SSLContext.getInstance("TLS");
+        serverContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null);
+
+        serverSslEngine = serverContext.createSSLEngine();
+
+        serverSslEngine.setNeedClientAuth(true);
+        serverSslEngine.setUseClientMode(false);
+        serverSslEngine.setEnabledProtocols(serverSslEngine.getSupportedProtocols());
+        serverSslEngine.setEnabledCipherSuites(serverSslEngine.getSupportedCipherSuites());
+        serverSslEngine.setEnableSessionCreation(true);
+    }
+
+    // **************
+    // Utility methods
+    // **************
+
+    public Map<String, Long> getMemory() {
+        Map<String, Long> m = new HashMap<>();
+        Runtime runtime = Runtime.getRuntime();
+        m.put("total", runtime.totalMemory());
+        m.put("free", runtime.freeMemory());
+        return m;
+    }
+
+
+    public Long getSystemUptime() {
+        RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean();
+        return rb.getUptime();
+    }
+
+    public long getSystemStartTime() {
+        return (this.systemStartTime);
+    }
+
+    /**
+     * Forward to the driver-manager to get an IOFSwitch instance.
+     *
+     * @param dpid data path id
+     * @param desc switch description
+     * @param ofv  OpenFlow version
+     * @return switch instance
+     */
+    protected OpenFlowSwitchDriver getOFSwitchInstance(long dpid,
+                                                       OFDescStatsReply desc,
+                                                       OFVersion ofv) {
+        Dpid dpidObj = new Dpid(dpid);
+
+        Driver driver;
+        try {
+            driver = driverService.getDriver(DeviceId.deviceId(Dpid.uri(dpidObj)));
+        } catch (ItemNotFoundException e) {
+            driver = driverService.getDriver(desc.getMfrDesc(), desc.getHwDesc(), desc.getSwDesc());
+        }
+
+        if (driver != null && driver.hasBehaviour(OpenFlowSwitchDriver.class)) {
+            Dpid did = new Dpid(dpid);
+            DefaultDriverHandler handler =
+                    new DefaultDriverHandler(new DefaultDriverData(driver, deviceId(uri(did))));
+            OpenFlowSwitchDriver ofSwitchDriver =
+                    driver.createBehaviour(handler, OpenFlowSwitchDriver.class);
+            ofSwitchDriver.init(did, desc, ofv);
+            ofSwitchDriver.setAgent(agent);
+            ofSwitchDriver.setRoleHandler(new RoleManager(ofSwitchDriver));
+            log.info("OpenFlow handshaker found for device {}: {}", dpid, ofSwitchDriver);
+            return ofSwitchDriver;
+        }
+        log.error("No OpenFlow driver for {} : {}", dpid, desc);
+        return null;
+
+    }
+
+    public void start(OpenFlowAgent ag, DriverService driverService) {
+        log.info("Starting OpenFlow IO");
+        this.agent = ag;
+        this.driverService = driverService;
+        this.init();
+        this.run();
+    }
+
+
+    public void stop() {
+        log.info("Stopping OpenFlow IO");
+        cg.close();
+        execFactory.shutdown();
+    }
+
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/HandshakeTimeoutException.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/HandshakeTimeoutException.java
new file mode 100644
index 0000000..bbe307b
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/HandshakeTimeoutException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.impl;
+
+/**
+ * Exception is thrown when the handshake fails to complete.
+ * before a specified time
+ *
+ */
+public class HandshakeTimeoutException extends Exception {
+
+    private static final long serialVersionUID = 6859880268940337312L;
+
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/HandshakeTimeoutHandler.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/HandshakeTimeoutHandler.java
new file mode 100644
index 0000000..fbbe342
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/HandshakeTimeoutHandler.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.impl;
+
+import java.util.concurrent.TimeUnit;
+
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.Timer;
+import org.jboss.netty.util.TimerTask;
+
+/**
+ * Trigger a timeout if a switch fails to complete handshake soon enough.
+ */
+public class HandshakeTimeoutHandler
+    extends SimpleChannelUpstreamHandler {
+    static final HandshakeTimeoutException EXCEPTION =
+            new HandshakeTimeoutException();
+
+    final OFChannelHandler channelHandler;
+    final Timer timer;
+    final long timeoutNanos;
+    volatile Timeout timeout;
+
+    public HandshakeTimeoutHandler(OFChannelHandler channelHandler,
+                                   Timer timer,
+                                   long timeoutSeconds) {
+        super();
+        this.channelHandler = channelHandler;
+        this.timer = timer;
+        this.timeoutNanos = TimeUnit.SECONDS.toNanos(timeoutSeconds);
+
+    }
+
+    @Override
+    public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
+            throws Exception {
+        if (timeoutNanos > 0) {
+            timeout = timer.newTimeout(new HandshakeTimeoutTask(ctx),
+                                       timeoutNanos, TimeUnit.NANOSECONDS);
+        }
+        ctx.sendUpstream(e);
+    }
+
+    @Override
+    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
+            throws Exception {
+        if (timeout != null) {
+            timeout.cancel();
+            timeout = null;
+        }
+    }
+
+    private final class HandshakeTimeoutTask implements TimerTask {
+
+        private final ChannelHandlerContext ctx;
+
+        HandshakeTimeoutTask(ChannelHandlerContext ctx) {
+            this.ctx = ctx;
+        }
+
+        @Override
+        public void run(Timeout t) throws Exception {
+            if (t.isCancelled()) {
+                return;
+            }
+
+            if (!ctx.getChannel().isOpen()) {
+                return;
+            }
+            if (!channelHandler.isHandshakeComplete()) {
+                Channels.fireExceptionCaught(ctx, EXCEPTION);
+            }
+        }
+    }
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
new file mode 100644
index 0000000..ff92b77
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java
@@ -0,0 +1,1320 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//CHECKSTYLE:OFF
+package org.onosproject.openflow.controller.impl;
+
+import java.io.IOException;
+import java.nio.channels.ClosedChannelException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.RejectedExecutionException;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler;
+import org.jboss.netty.handler.timeout.IdleStateEvent;
+import org.jboss.netty.handler.timeout.ReadTimeoutException;
+import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
+import org.onosproject.openflow.controller.driver.SwitchStateException;
+import org.projectfloodlight.openflow.exceptions.OFParseError;
+import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
+import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
+import org.projectfloodlight.openflow.protocol.OFBarrierReply;
+import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFDescStatsRequest;
+import org.projectfloodlight.openflow.protocol.OFEchoReply;
+import org.projectfloodlight.openflow.protocol.OFEchoRequest;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFErrorType;
+import org.projectfloodlight.openflow.protocol.OFExperimenter;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFFlowModFailedCode;
+import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
+import org.projectfloodlight.openflow.protocol.OFGetConfigReply;
+import org.projectfloodlight.openflow.protocol.OFGetConfigRequest;
+import org.projectfloodlight.openflow.protocol.OFHello;
+import org.projectfloodlight.openflow.protocol.OFHelloElem;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFPortDescStatsRequest;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFQueueGetConfigReply;
+import org.projectfloodlight.openflow.protocol.OFRoleReply;
+import org.projectfloodlight.openflow.protocol.OFSetConfig;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
+import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
+import org.projectfloodlight.openflow.types.U32;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Channel handler deals with the switch connection and dispatches
+ * switch messages to the appropriate locations.
+ */
+class OFChannelHandler extends IdleStateAwareChannelHandler {
+    private static final Logger log = LoggerFactory.getLogger(OFChannelHandler.class);
+
+    private static final String RESET_BY_PEER = "Connection reset by peer";
+    private static final String BROKEN_PIPE = "Broken pipe";
+
+    private final Controller controller;
+    private OpenFlowSwitchDriver sw;
+    private long thisdpid; // channelHandler cached value of connected switch id
+    private Channel channel;
+    // State needs to be volatile because the HandshakeTimeoutHandler
+    // needs to check if the handshake is complete
+    private volatile ChannelState state;
+
+    // When a switch with a duplicate dpid is found (i.e we already have a
+    // connected switch with the same dpid), the new switch is immediately
+    // disconnected. At that point netty callsback channelDisconnected() which
+    // proceeds to cleaup switch state - we need to ensure that it does not cleanup
+    // switch state for the older (still connected) switch
+    private volatile Boolean duplicateDpidFound;
+
+    // Temporary storage for switch-features and port-description
+    private OFFeaturesReply featuresReply;
+    private List<OFPortDescStatsReply> portDescReplies;
+    //private OFPortDescStatsReply portDescReply;
+    // a concurrent ArrayList to temporarily store port status messages
+    // before we are ready to deal with them
+    private final CopyOnWriteArrayList<OFPortStatus> pendingPortStatusMsg;
+
+    //Indicates the openflow version used by this switch
+    protected OFVersion ofVersion;
+    protected OFFactory factory13;
+    protected OFFactory factory10;
+
+    /** transaction Ids to use during handshake. Since only one thread
+     * calls into an OFChannelHandler instance, we don't need atomic.
+     * We will count down
+     */
+    private int handshakeTransactionIds = -1;
+
+    /**
+     * Create a new unconnected OFChannelHandler.
+     * @param controller parent controller
+     */
+    OFChannelHandler(Controller controller) {
+        this.controller = controller;
+        this.state = ChannelState.INIT;
+        this.pendingPortStatusMsg = new CopyOnWriteArrayList<OFPortStatus>();
+        this.portDescReplies = new ArrayList<OFPortDescStatsReply>();
+        factory13 = controller.getOFMessageFactory13();
+        factory10 = controller.getOFMessageFactory10();
+        duplicateDpidFound = Boolean.FALSE;
+    }
+
+
+
+    // XXX S consider if necessary
+    public void disconnectSwitch() {
+        sw.disconnectSwitch();
+    }
+
+
+
+    //*************************
+    //  Channel State Machine
+    //*************************
+
+    /**
+     * The state machine for handling the switch/channel state. All state
+     * transitions should happen from within the state machine (and not from other
+     * parts of the code)
+     */
+    enum ChannelState {
+        /**
+         * Initial state before channel is connected.
+         */
+        INIT(false) {
+            @Override
+            void processOFMessage(OFChannelHandler h, OFMessage m)
+                    throws IOException, SwitchStateException {
+                illegalMessageReceived(h, m);
+            }
+
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m)
+                    throws IOException {
+                // need to implement since its abstract but it will never
+                // be called
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException {
+                unhandledMessageReceived(h, m);
+            }
+        },
+
+        /**
+         * We send a OF 1.3 HELLO to the switch and wait for a Hello from the switch.
+         * Once we receive the reply, we decide on OF 1.3 or 1.0 switch - no other
+         * protocol version is accepted.
+         * We send an OFFeaturesRequest depending on the protocol version selected
+         * Next state is WAIT_FEATURES_REPLY
+         */
+        WAIT_HELLO(false) {
+            @Override
+            void processOFHello(OFChannelHandler h, OFHello m)
+                    throws IOException {
+                // TODO We could check for the optional bitmap, but for now
+                // we are just checking the version number.
+                if (m.getVersion().getWireVersion() >= OFVersion.OF_13.getWireVersion()) {
+                    log.debug("Received {} Hello from {} - switching to OF "
+                            + "version 1.3", m.getVersion(),
+                            h.channel.getRemoteAddress());
+                    h.sendHandshakeHelloMessage();
+                    h.ofVersion = OFVersion.OF_13;
+                } else if (m.getVersion().getWireVersion() >= OFVersion.OF_10.getWireVersion()) {
+                    log.debug("Received {} Hello from {} - switching to OF "
+                            + "version 1.0", m.getVersion(),
+                            h.channel.getRemoteAddress());
+                    OFHello hi =
+                            h.factory10.buildHello()
+                                    .setXid(h.handshakeTransactionIds--)
+                                    .build();
+                    h.channel.write(Collections.singletonList(hi));
+                    h.ofVersion = OFVersion.OF_10;
+                } else {
+                    log.error("Received Hello of version {} from switch at {}. "
+                            + "This controller works with OF1.0 and OF1.3 "
+                            + "switches. Disconnecting switch ...",
+                            m.getVersion(), h.channel.getRemoteAddress());
+                    h.channel.disconnect();
+                    return;
+                }
+                h.sendHandshakeFeaturesRequestMessage();
+                h.setState(WAIT_FEATURES_REPLY);
+            }
+            @Override
+            void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply  m)
+                    throws IOException, SwitchStateException {
+                illegalMessageReceived(h, m);
+            }
+            @Override
+            void processOFStatisticsReply(OFChannelHandler h,
+                    OFStatsReply  m)
+                            throws IOException, SwitchStateException {
+                illegalMessageReceived(h, m);
+            }
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m) {
+                logErrorDisconnect(h, m);
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException {
+                unhandledMessageReceived(h, m);
+            }
+        },
+
+
+        /**
+         * We are waiting for a features reply message. Once we receive it, the
+         * behavior depends on whether this is a 1.0 or 1.3 switch. For 1.0,
+         * we send a SetConfig request, barrier, and GetConfig request and the
+         * next state is WAIT_CONFIG_REPLY. For 1.3, we send a Port description
+         * request and the next state is WAIT_PORT_DESC_REPLY.
+         */
+        WAIT_FEATURES_REPLY(false) {
+            @Override
+            void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply  m)
+                    throws IOException {
+                h.thisdpid = m.getDatapathId().getLong();
+                log.debug("Received features reply for switch at {} with dpid {}",
+                        h.getSwitchInfoString(), h.thisdpid);
+
+                h.featuresReply = m; //temp store
+                if (h.ofVersion == OFVersion.OF_10) {
+                    h.sendHandshakeSetConfig();
+                    h.setState(WAIT_CONFIG_REPLY);
+                } else {
+                    //version is 1.3, must get switchport information
+                    h.sendHandshakeOFPortDescRequest();
+                    h.setState(WAIT_PORT_DESC_REPLY);
+                }
+            }
+            @Override
+            void processOFStatisticsReply(OFChannelHandler h,
+                    OFStatsReply  m)
+                            throws IOException, SwitchStateException {
+                illegalMessageReceived(h, m);
+            }
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m) {
+                logErrorDisconnect(h, m);
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException {
+                h.pendingPortStatusMsg.add(m);
+            }
+        },
+
+        /**
+         * We are waiting for a description of the 1.3 switch ports.
+         * Once received, we send a SetConfig request
+         * Next State is WAIT_CONFIG_REPLY
+         */
+        WAIT_PORT_DESC_REPLY(false) {
+
+            @Override
+            void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
+                    throws SwitchStateException {
+                // Read port description
+                if (m.getStatsType() != OFStatsType.PORT_DESC) {
+                    log.warn("Expecting port description stats but received stats "
+                            + "type {} from {}. Ignoring ...", m.getStatsType(),
+                            h.channel.getRemoteAddress());
+                    return;
+                }
+                if (m.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+                    log.debug("Stats reply indicates more stats from sw {} for "
+                            + "port description",
+                            h.getSwitchInfoString());
+                    h.portDescReplies.add((OFPortDescStatsReply)m);
+                    return;
+                }
+                else {
+                    h.portDescReplies.add((OFPortDescStatsReply)m);
+                }
+                //h.portDescReply = (OFPortDescStatsReply) m; // temp store
+                log.info("Received port desc reply for switch at {}",
+                        h.getSwitchInfoString());
+                try {
+                    h.sendHandshakeSetConfig();
+                } catch (IOException e) {
+                    log.error("Unable to send setConfig after PortDescReply. "
+                            + "Error: {}", e.getMessage());
+                }
+                h.setState(WAIT_CONFIG_REPLY);
+            }
+
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m)
+                    throws IOException, SwitchStateException {
+                logErrorDisconnect(h, m);
+
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException, SwitchStateException {
+                h.pendingPortStatusMsg.add(m);
+
+            }
+        },
+
+        /**
+         * We are waiting for a config reply message. Once we receive it
+         * we send a DescriptionStatsRequest to the switch.
+         * Next state: WAIT_DESCRIPTION_STAT_REPLY
+         */
+        WAIT_CONFIG_REPLY(false) {
+            @Override
+            void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
+                    throws IOException {
+                if (m.getMissSendLen() == 0xffff) {
+                    log.trace("Config Reply from switch {} confirms "
+                            + "miss length set to 0xffff",
+                            h.getSwitchInfoString());
+                } else {
+                    // FIXME: we can't really deal with switches that don't send
+                    // full packets. Shouldn't we drop the connection here?
+                    log.warn("Config Reply from switch {} has"
+                            + "miss length set to {}",
+                            h.getSwitchInfoString(),
+                            m.getMissSendLen());
+                }
+                h.sendHandshakeDescriptionStatsRequest();
+                h.setState(WAIT_DESCRIPTION_STAT_REPLY);
+            }
+
+            @Override
+            void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
+                // do nothing;
+            }
+
+            @Override
+            void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply  m)
+                    throws IOException, SwitchStateException {
+                illegalMessageReceived(h, m);
+            }
+            @Override
+            void processOFStatisticsReply(OFChannelHandler h,
+                    OFStatsReply  m)
+                            throws IOException, SwitchStateException {
+                log.error("Received multipart(stats) message sub-type {}",
+                        m.getStatsType());
+                illegalMessageReceived(h, m);
+            }
+
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m) {
+                logErrorDisconnect(h, m);
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException {
+                h.pendingPortStatusMsg.add(m);
+            }
+        },
+
+
+        /**
+         * We are waiting for a OFDescriptionStat message from the switch.
+         * Once we receive any stat message we try to parse it. If it's not
+         * a description stats message we disconnect. If its the expected
+         * description stats message, we:
+         *    - use the switch driver to bind the switch and get an IOFSwitch instance
+         *    - setup the IOFSwitch instance
+         *    - add switch controller and send the initial role
+         *      request to the switch.
+         * Next state: WAIT_INITIAL_ROLE
+         *      In the typical case, where switches support role request messages
+         *      the next state is where we expect the role reply message.
+         *      In the special case that where the switch does not support any kind
+         *      of role request messages, we don't send a role message, but we do
+         *      request mastership from the registry service. This controller
+         *      should become master once we hear back from the registry service.
+         * All following states will have a h.sw instance!
+         */
+        WAIT_DESCRIPTION_STAT_REPLY(false) {
+            @Override
+            void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
+                    throws SwitchStateException {
+                // Read description, if it has been updated
+                if (m.getStatsType() != OFStatsType.DESC) {
+                    log.warn("Expecting Description stats but received stats "
+                            + "type {} from {}. Ignoring ...", m.getStatsType(),
+                            h.channel.getRemoteAddress());
+                    return;
+                }
+                OFDescStatsReply drep = (OFDescStatsReply) m;
+                log.info("Received switch description reply {} from switch at {}",
+                         drep, h.channel.getRemoteAddress());
+                // Here is where we differentiate between different kinds of switches
+                h.sw = h.controller.getOFSwitchInstance(h.thisdpid, drep, h.ofVersion);
+
+                h.sw.setOFVersion(h.ofVersion);
+                h.sw.setFeaturesReply(h.featuresReply);
+                //h.sw.setPortDescReply(h.portDescReply);
+                h.sw.setPortDescReplies(h.portDescReplies);
+                h.sw.setConnected(true);
+                h.sw.setChannel(h.channel);
+//                boolean success = h.sw.connectSwitch();
+//
+//                if (!success) {
+//                    disconnectDuplicate(h);
+//                    return;
+//                }
+                // set switch information
+
+
+
+                log.debug("Switch {} bound to class {}, description {}",
+                        h.sw, h.sw.getClass(), drep);
+                //Put switch in EQUAL mode until we hear back from the global registry
+                //log.debug("Setting new switch {} to EQUAL and sending Role request",
+                //        h.sw.getStringId());
+                //h.sw.activateEqualSwitch();
+                //h.setSwitchRole(RoleState.EQUAL);
+
+                h.sw.startDriverHandshake();
+                if (h.sw.isDriverHandshakeComplete()) {
+                    if (!h.sw.connectSwitch()) {
+                        disconnectDuplicate(h);
+                    }
+                    handlePendingPortStatusMessages(h);
+                    h.setState(ACTIVE);
+                } else {
+                    h.setState(WAIT_SWITCH_DRIVER_SUB_HANDSHAKE);
+                }
+
+            }
+
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m) {
+                logErrorDisconnect(h, m);
+            }
+
+            @Override
+            void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply  m)
+                    throws IOException, SwitchStateException {
+                illegalMessageReceived(h, m);
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException {
+                h.pendingPortStatusMsg.add(m);
+            }
+        },
+
+
+        /**
+         * We are waiting for the respective switch driver to complete its
+         * configuration. Notice that we do not consider this to be part of the main
+         * switch-controller handshake. But we do consider it as a step that comes
+         * before we declare the switch as available to the controller.
+         * Next State: depends on the role of this controller for this switch - either
+         * MASTER or EQUAL.
+         */
+        WAIT_SWITCH_DRIVER_SUB_HANDSHAKE(true) {
+
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m)
+                    throws IOException {
+                // will never be called. We override processOFMessage
+            }
+
+
+
+            @Override
+            void processOFMessage(OFChannelHandler h, OFMessage m)
+                    throws IOException, SwitchStateException {
+
+                if (h.sw.isDriverHandshakeComplete()) {
+                    moveToActive(h);
+                    h.state.processOFMessage(h, m);
+                    return;
+
+                }
+
+                if (m.getType() == OFType.ECHO_REQUEST) {
+                    processOFEchoRequest(h, (OFEchoRequest) m);
+                } else if (m.getType() == OFType.ECHO_REPLY) {
+                    processOFEchoReply(h, (OFEchoReply) m);
+                } else if (m.getType() == OFType.ROLE_REPLY) {
+                    h.sw.handleRole(m);
+                } else if (m.getType() == OFType.ERROR) {
+                    if (!h.sw.handleRoleError((OFErrorMsg)m)) {
+                        h.sw.processDriverHandshakeMessage(m);
+                        if (h.sw.isDriverHandshakeComplete()) {
+                            moveToActive(h);
+                        }
+                    }
+                } else {
+                    if (m.getType() == OFType.EXPERIMENTER &&
+                            ((OFExperimenter) m).getExperimenter() ==
+                            RoleManager.NICIRA_EXPERIMENTER) {
+                        h.sw.handleNiciraRole(m);
+                    } else {
+                        h.sw.processDriverHandshakeMessage(m);
+                        if (h.sw.isDriverHandshakeComplete()) {
+                            moveToActive(h);
+                        }
+                    }
+                }
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws IOException, SwitchStateException {
+                h.pendingPortStatusMsg.add(m);
+            }
+
+            private void moveToActive(OFChannelHandler h) {
+                boolean success = h.sw.connectSwitch();
+                handlePendingPortStatusMessages(h);
+                h.setState(ACTIVE);
+                if (!success) {
+                    disconnectDuplicate(h);
+                }
+            }
+
+        },
+
+
+        /**
+         * This controller is in MASTER role for this switch. We enter this state
+         * after requesting and winning control from the global registry.
+         * The main handshake as well as the switch-driver sub-handshake
+         * is complete at this point.
+         * // XXX S reconsider below
+         * In the (near) future we may deterministically assign controllers to
+         * switches at startup.
+         * We only leave this state if the switch disconnects or
+         * if we send a role request for SLAVE /and/ receive the role reply for
+         * SLAVE.
+         */
+        ACTIVE(true) {
+            @Override
+            void processOFError(OFChannelHandler h, OFErrorMsg m)
+                    throws IOException, SwitchStateException {
+                // if we get here, then the error message is for something else
+                if (m.getErrType() == OFErrorType.BAD_REQUEST &&
+                        ((OFBadRequestErrorMsg) m).getCode() ==
+                        OFBadRequestCode.EPERM) {
+                    // We are the master controller and the switch returned
+                    // a permission error. This is a likely indicator that
+                    // the switch thinks we are slave. Reassert our
+                    // role
+                    // FIXME: this could be really bad during role transitions
+                    // if two controllers are master (even if its only for
+                    // a brief period). We might need to see if these errors
+                    // persist before we reassert
+
+                    h.sw.reassertRole();
+                } else if (m.getErrType() == OFErrorType.FLOW_MOD_FAILED &&
+                        ((OFFlowModFailedErrorMsg) m).getCode() ==
+                        OFFlowModFailedCode.ALL_TABLES_FULL) {
+                    h.sw.setTableFull(true);
+                } else {
+                    logError(h, m);
+                }
+                h.dispatchMessage(m);
+            }
+
+            @Override
+            void processOFStatisticsReply(OFChannelHandler h,
+                    OFStatsReply m) {
+                if (m.getStatsType().equals(OFStatsType.PORT_DESC)) {
+                    h.sw.setPortDescReply((OFPortDescStatsReply) m);
+                }
+                h.dispatchMessage(m);
+            }
+
+            @Override
+            void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
+                    throws SwitchStateException {
+                h.sw.handleNiciraRole(m);
+            }
+
+            @Override
+            void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
+                    throws SwitchStateException {
+                h.sw.handleRole(m);
+            }
+
+            @Override
+            void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                    throws SwitchStateException {
+                handlePortStatusMessage(h, m, true);
+                //h.dispatchMessage(m);
+            }
+
+            @Override
+            void processOFPacketIn(OFChannelHandler h, OFPacketIn m) {
+//                OFPacketOut out =
+//                        h.sw.factory().buildPacketOut()
+//                                .setXid(m.getXid())
+//                                .setBufferId(m.getBufferId()).build();
+//                h.sw.sendMsg(out);
+                h.dispatchMessage(m);
+            }
+
+            @Override
+            void processOFFlowRemoved(OFChannelHandler h,
+                    OFFlowRemoved m) {
+                h.dispatchMessage(m);
+            }
+
+            @Override
+            void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) {
+                h.dispatchMessage(m);
+            }
+
+            @Override
+            void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply  m) {
+                h.sw.setFeaturesReply(m);
+                h.dispatchMessage(m);
+            }
+
+        };
+
+        private final boolean handshakeComplete;
+        ChannelState(boolean handshakeComplete) {
+            this.handshakeComplete = handshakeComplete;
+        }
+
+        /**
+         * Is this a state in which the handshake has completed?
+         * @return true if the handshake is complete
+         */
+        public boolean isHandshakeComplete() {
+            return handshakeComplete;
+        }
+
+        /**
+         * Get a string specifying the switch connection, state, and
+         * message received. To be used as message for SwitchStateException
+         * or log messages
+         * @param h The channel handler (to get switch information_
+         * @param m The OFMessage that has just been received
+         * @param details A string giving more details about the exact nature
+         * of the problem.
+         * @return display string
+         */
+        // needs to be protected because enum members are actually subclasses
+        protected String getSwitchStateMessage(OFChannelHandler h,
+                OFMessage m,
+                String details) {
+            return String.format("Switch: [%s], State: [%s], received: [%s]"
+                    + ", details: %s",
+                    h.getSwitchInfoString(),
+                    this.toString(),
+                    m.getType().toString(),
+                    details);
+        }
+
+        /**
+         * We have an OFMessage we didn't expect given the current state and
+         * we want to treat this as an error.
+         * We currently throw an exception that will terminate the connection
+         * However, we could be more forgiving
+         * @param h the channel handler that received the message
+         * @param m the message
+         * @throws SwitchStateException we always throw the exception
+         */
+        // needs to be protected because enum members are actually subclasses
+        protected void illegalMessageReceived(OFChannelHandler h, OFMessage m)
+                throws SwitchStateException {
+            String msg = getSwitchStateMessage(h, m,
+                    "Switch should never send this message in the current state");
+            throw new SwitchStateException(msg);
+
+        }
+
+        /**
+         * We have an OFMessage we didn't expect given the current state and
+         * we want to ignore the message.
+         * @param h the channel handler the received the message
+         * @param m the message
+         */
+        protected void unhandledMessageReceived(OFChannelHandler h,
+                OFMessage m) {
+            if (log.isDebugEnabled()) {
+                String msg = getSwitchStateMessage(h, m,
+                        "Ignoring unexpected message");
+                log.debug(msg);
+            }
+        }
+
+        /**
+         * Log an OpenFlow error message from a switch.
+         * @param h The switch that sent the error
+         * @param error The error message
+         */
+        protected void logError(OFChannelHandler h, OFErrorMsg error) {
+            log.error("{} from switch {} in state {}",
+                    error,
+                    h.getSwitchInfoString(),
+                    this.toString());
+        }
+
+        /**
+         * Log an OpenFlow error message from a switch and disconnect the
+         * channel.
+         *
+         * @param h the IO channel for this switch.
+         * @param error The error message
+         */
+        protected void logErrorDisconnect(OFChannelHandler h, OFErrorMsg error) {
+            logError(h, error);
+            h.channel.disconnect();
+        }
+
+        /**
+         * log an error message for a duplicate dpid and disconnect this channel.
+         * @param h the IO channel for this switch.
+         */
+        protected void disconnectDuplicate(OFChannelHandler h) {
+            log.error("Duplicated dpid or incompleted cleanup - "
+                    + "disconnecting channel {}", h.getSwitchInfoString());
+            h.duplicateDpidFound = Boolean.TRUE;
+            h.channel.disconnect();
+        }
+
+
+
+        /**
+         * Handles all pending port status messages before a switch is declared
+         * activated in MASTER or EQUAL role. Note that since this handling
+         * precedes the activation (and therefore notification to IOFSwitchListerners)
+         * the changes to ports will already be visible once the switch is
+         * activated. As a result, no notifications are sent out for these
+         * pending portStatus messages.
+         *
+         * @param h the channel handler that received the message
+         */
+        protected void handlePendingPortStatusMessages(OFChannelHandler h) {
+            try {
+                handlePendingPortStatusMessages(h, 0);
+            } catch (SwitchStateException e) {
+                log.error(e.getMessage());
+            }
+        }
+
+        private void handlePendingPortStatusMessages(OFChannelHandler h, int index)
+                throws SwitchStateException {
+            if (h.sw == null) {
+                String msg = "State machine error: switch is null. Should never " +
+                        "happen";
+                throw new SwitchStateException(msg);
+            }
+            log.info("Processing {} pending port status messages for {}",
+                     h.pendingPortStatusMsg.size(), h.sw.getStringId());
+
+            ArrayList<OFPortStatus> temp  = new ArrayList<OFPortStatus>();
+            for (OFPortStatus ps: h.pendingPortStatusMsg) {
+                temp.add(ps);
+                handlePortStatusMessage(h, ps, false);
+            }
+            // expensive but ok - we don't expect too many port-status messages
+            // note that we cannot use clear(), because of the reasons below
+            h.pendingPortStatusMsg.removeAll(temp);
+            temp.clear();
+            // the iterator above takes a snapshot of the list - so while we were
+            // dealing with the pending port-status messages, we could have received
+            // newer ones. Handle them recursively, but break the recursion after
+            // five steps to avoid an attack.
+            if (!h.pendingPortStatusMsg.isEmpty() && ++index < 5) {
+                handlePendingPortStatusMessages(h, index);
+            }
+        }
+
+        /**
+         * Handle a port status message.
+         *
+         * Handle a port status message by updating the port maps in the
+         * IOFSwitch instance and notifying Controller about the change so
+         * it can dispatch a switch update.
+         *
+         * @param h The OFChannelHhandler that received the message
+         * @param m The PortStatus message we received
+         * @param doNotify if true switch port changed events will be
+         * dispatched
+         * @throws SwitchStateException if the switch is not bound to the channel
+         *
+         */
+        protected void handlePortStatusMessage(OFChannelHandler h, OFPortStatus m,
+                boolean doNotify) throws SwitchStateException {
+            if (h.sw == null) {
+                String msg = getSwitchStateMessage(h, m,
+                        "State machine error: switch is null. Should never " +
+                        "happen");
+                throw new SwitchStateException(msg);
+            }
+
+            h.sw.handleMessage(m);
+        }
+
+
+        /**
+         * Process an OF message received on the channel and
+         * update state accordingly.
+         *
+         * The main "event" of the state machine. Process the received message,
+         * send follow up message if required and update state if required.
+         *
+         * Switches on the message type and calls more specific event handlers
+         * for each individual OF message type. If we receive a message that
+         * is supposed to be sent from a controller to a switch we throw
+         * a SwitchStateExeption.
+         *
+         * The more specific handlers can also throw SwitchStateExceptions
+         *
+         * @param h The OFChannelHandler that received the message
+         * @param m The message we received.
+         * @throws SwitchStateException if the switch is not bound to the channel
+         * @throws IOException if unable to send message back to the switch
+         */
+        void processOFMessage(OFChannelHandler h, OFMessage m)
+                throws IOException, SwitchStateException {
+            switch(m.getType()) {
+            case HELLO:
+                processOFHello(h, (OFHello) m);
+                break;
+            case BARRIER_REPLY:
+                processOFBarrierReply(h, (OFBarrierReply) m);
+                break;
+            case ECHO_REPLY:
+                processOFEchoReply(h, (OFEchoReply) m);
+                break;
+            case ECHO_REQUEST:
+                processOFEchoRequest(h, (OFEchoRequest) m);
+                break;
+            case ERROR:
+                processOFError(h, (OFErrorMsg) m);
+                break;
+            case FEATURES_REPLY:
+                processOFFeaturesReply(h, (OFFeaturesReply) m);
+                break;
+            case FLOW_REMOVED:
+                processOFFlowRemoved(h, (OFFlowRemoved) m);
+                break;
+            case GET_CONFIG_REPLY:
+                processOFGetConfigReply(h, (OFGetConfigReply) m);
+                break;
+            case PACKET_IN:
+                processOFPacketIn(h, (OFPacketIn) m);
+                break;
+            case PORT_STATUS:
+                processOFPortStatus(h, (OFPortStatus) m);
+                break;
+            case QUEUE_GET_CONFIG_REPLY:
+                processOFQueueGetConfigReply(h, (OFQueueGetConfigReply) m);
+                break;
+            case STATS_REPLY: // multipart_reply in 1.3
+                processOFStatisticsReply(h, (OFStatsReply) m);
+                break;
+            case EXPERIMENTER:
+                processOFExperimenter(h, (OFExperimenter) m);
+                break;
+            case ROLE_REPLY:
+                processOFRoleReply(h, (OFRoleReply) m);
+                break;
+            case GET_ASYNC_REPLY:
+                processOFGetAsyncReply(h, (OFAsyncGetReply) m);
+                break;
+
+                // The following messages are sent to switches. The controller
+                // should never receive them
+            case SET_CONFIG:
+            case GET_CONFIG_REQUEST:
+            case PACKET_OUT:
+            case PORT_MOD:
+            case QUEUE_GET_CONFIG_REQUEST:
+            case BARRIER_REQUEST:
+            case STATS_REQUEST: // multipart request in 1.3
+            case FEATURES_REQUEST:
+            case FLOW_MOD:
+            case GROUP_MOD:
+            case TABLE_MOD:
+            case GET_ASYNC_REQUEST:
+            case SET_ASYNC:
+            case METER_MOD:
+            default:
+                illegalMessageReceived(h, m);
+                break;
+            }
+        }
+
+        /*-----------------------------------------------------------------
+         * Default implementation for message handlers in any state.
+         *
+         * Individual states must override these if they want a behavior
+         * that differs from the default.
+         *
+         * In general, these handlers simply ignore the message and do
+         * nothing.
+         *
+         * There are some exceptions though, since some messages really
+         * are handled the same way in every state (e.g., ECHO_REQUST) or
+         * that are only valid in a single state (e.g., HELLO, GET_CONFIG_REPLY
+         -----------------------------------------------------------------*/
+
+        void processOFHello(OFChannelHandler h, OFHello m)
+                throws IOException, SwitchStateException {
+            // we only expect hello in the WAIT_HELLO state
+            log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.",
+                     h.channel.getRemoteAddress());
+        }
+
+        void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m)
+                throws IOException {
+            // Silently ignore.
+        }
+
+        void processOFEchoRequest(OFChannelHandler h, OFEchoRequest m)
+                throws IOException {
+            if (h.ofVersion == null) {
+                log.error("No OF version set for {}. Not sending Echo REPLY",
+                        h.channel.getRemoteAddress());
+                return;
+            }
+            OFFactory factory = (h.ofVersion == OFVersion.OF_13) ?
+                    h.controller.getOFMessageFactory13() : h.controller.getOFMessageFactory10();
+                    OFEchoReply reply = factory
+                            .buildEchoReply()
+                            .setXid(m.getXid())
+                            .setData(m.getData())
+                            .build();
+                    h.channel.write(Collections.singletonList(reply));
+        }
+
+        void processOFEchoReply(OFChannelHandler h, OFEchoReply m)
+                throws IOException {
+            // Do nothing with EchoReplies !!
+        }
+
+        // no default implementation for OFError
+        // every state must override it
+        abstract void processOFError(OFChannelHandler h, OFErrorMsg m)
+                throws IOException, SwitchStateException;
+
+
+        void processOFFeaturesReply(OFChannelHandler h, OFFeaturesReply  m)
+                throws IOException, SwitchStateException {
+            unhandledMessageReceived(h, m);
+        }
+
+        void processOFFlowRemoved(OFChannelHandler h, OFFlowRemoved m)
+                throws IOException {
+            unhandledMessageReceived(h, m);
+        }
+
+        void processOFGetConfigReply(OFChannelHandler h, OFGetConfigReply m)
+                throws IOException, SwitchStateException {
+            // we only expect config replies in the WAIT_CONFIG_REPLY state
+            illegalMessageReceived(h, m);
+        }
+
+        void processOFPacketIn(OFChannelHandler h, OFPacketIn m)
+                throws IOException {
+            unhandledMessageReceived(h, m);
+        }
+
+        // no default implementation. Every state needs to handle it.
+        abstract void processOFPortStatus(OFChannelHandler h, OFPortStatus m)
+                throws IOException, SwitchStateException;
+
+        void processOFQueueGetConfigReply(OFChannelHandler h,
+                OFQueueGetConfigReply m)
+                        throws IOException {
+            unhandledMessageReceived(h, m);
+        }
+
+        void processOFStatisticsReply(OFChannelHandler h, OFStatsReply m)
+                throws IOException, SwitchStateException {
+            unhandledMessageReceived(h, m);
+        }
+
+        void processOFExperimenter(OFChannelHandler h, OFExperimenter m)
+                throws IOException, SwitchStateException {
+            // TODO: it might make sense to parse the vendor message here
+            // into the known vendor messages we support and then call more
+            // specific event handlers
+            unhandledMessageReceived(h, m);
+        }
+
+        void processOFRoleReply(OFChannelHandler h, OFRoleReply m)
+                throws SwitchStateException, IOException {
+            unhandledMessageReceived(h, m);
+        }
+
+        void processOFGetAsyncReply(OFChannelHandler h,
+                OFAsyncGetReply m) {
+            unhandledMessageReceived(h, m);
+        }
+
+    }
+
+
+
+    //*************************
+    //  Channel handler methods
+    //*************************
+
+    @Override
+    public void channelConnected(ChannelHandlerContext ctx,
+            ChannelStateEvent e) throws Exception {
+        channel = e.getChannel();
+        log.info("New switch connection from {}",
+                channel.getRemoteAddress());
+        /*
+            hack to wait for the switch to tell us what it's
+            max version is. This is not spec compliant and should
+            be removed as soon as switches behave better.
+         */
+        //sendHandshakeHelloMessage();
+        setState(ChannelState.WAIT_HELLO);
+    }
+
+    @Override
+    public void channelDisconnected(ChannelHandlerContext ctx,
+            ChannelStateEvent e) throws Exception {
+        log.info("Switch disconnected callback for sw:{}. Cleaning up ...",
+                getSwitchInfoString());
+        if (thisdpid != 0) {
+            if (!duplicateDpidFound) {
+                // if the disconnected switch (on this ChannelHandler)
+                // was not one with a duplicate-dpid, it is safe to remove all
+                // state for it at the controller. Notice that if the disconnected
+                // switch was a duplicate-dpid, calling the method below would clear
+                // all state for the original switch (with the same dpid),
+                // which we obviously don't want.
+                log.info("{}:removal called", getSwitchInfoString());
+                if (sw != null) {
+                    sw.removeConnectedSwitch();
+                }
+            } else {
+                // A duplicate was disconnected on this ChannelHandler,
+                // this is the same switch reconnecting, but the original state was
+                // not cleaned up - XXX check liveness of original ChannelHandler
+                log.info("{}:duplicate found", getSwitchInfoString());
+                duplicateDpidFound = Boolean.FALSE;
+            }
+        } else {
+            log.warn("no dpid in channelHandler registered for "
+                    + "disconnected switch {}", getSwitchInfoString());
+        }
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
+            throws Exception {
+        if (e.getCause() instanceof ReadTimeoutException) {
+            // switch timeout
+            log.error("Disconnecting switch {} due to read timeout",
+                    getSwitchInfoString());
+            ctx.getChannel().close();
+        } else if (e.getCause() instanceof HandshakeTimeoutException) {
+            log.error("Disconnecting switch {}: failed to complete handshake",
+                    getSwitchInfoString());
+            ctx.getChannel().close();
+        } else if (e.getCause() instanceof ClosedChannelException) {
+            log.debug("Channel for sw {} already closed", getSwitchInfoString());
+        } else if (e.getCause() instanceof IOException) {
+            if (!e.getCause().getMessage().equals(RESET_BY_PEER) &&
+                    !e.getCause().getMessage().equals(BROKEN_PIPE)) {
+                log.error("Disconnecting switch {} due to IO Error: {}",
+                          getSwitchInfoString(), e.getCause().getMessage());
+                if (log.isDebugEnabled()) {
+                    // still print stack trace if debug is enabled
+                    log.debug("StackTrace for previous Exception: ", e.getCause());
+                }
+            }
+            ctx.getChannel().close();
+        } else if (e.getCause() instanceof SwitchStateException) {
+            log.error("Disconnecting switch {} due to switch state error: {}",
+                    getSwitchInfoString(), e.getCause().getMessage());
+            if (log.isDebugEnabled()) {
+                // still print stack trace if debug is enabled
+                log.debug("StackTrace for previous Exception: ", e.getCause());
+            }
+            ctx.getChannel().close();
+        } else if (e.getCause() instanceof OFParseError) {
+            log.error("Disconnecting switch "
+                    + getSwitchInfoString() +
+                    " due to message parse failure",
+                    e.getCause());
+            ctx.getChannel().close();
+        } else if (e.getCause() instanceof RejectedExecutionException) {
+            log.warn("Could not process message: queue full");
+        } else {
+            log.error("Error while processing message from switch "
+                    + getSwitchInfoString()
+                    + "state " + this.state, e.getCause());
+            ctx.getChannel().close();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getSwitchInfoString();
+    }
+
+    @Override
+    public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e)
+            throws Exception {
+        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFMessage m = factory.buildEchoRequest().build();
+        log.debug("Sending Echo Request on idle channel: {}",
+                e.getChannel().getPipeline().getLast().toString());
+        e.getChannel().write(Collections.singletonList(m));
+        // XXX S some problems here -- echo request has no transaction id, and
+        // echo reply is not correlated to the echo request.
+    }
+
+    @Override
+    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
+            throws Exception {
+        if (e.getMessage() instanceof List) {
+            @SuppressWarnings("unchecked")
+            List<OFMessage> msglist = (List<OFMessage>) e.getMessage();
+
+
+            for (OFMessage ofm : msglist) {
+                // Do the actual packet processing
+                state.processOFMessage(this, ofm);
+            }
+        } else {
+            state.processOFMessage(this, (OFMessage) e.getMessage());
+        }
+    }
+
+
+
+    //*************************
+    //  Channel utility methods
+    //*************************
+
+    /**
+     * Is this a state in which the handshake has completed?
+     * @return true if the handshake is complete
+     */
+    public boolean isHandshakeComplete() {
+        return this.state.isHandshakeComplete();
+    }
+
+    private void dispatchMessage(OFMessage m) {
+        sw.handleMessage(m);
+    }
+
+    /**
+     * Return a string describing this switch based on the already available
+     * information (DPID and/or remote socket).
+     * @return display string
+     */
+    private String getSwitchInfoString() {
+        if (sw != null) {
+            return sw.toString();
+        }
+        String channelString;
+        if (channel == null || channel.getRemoteAddress() == null) {
+            channelString = "?";
+        } else {
+            channelString = channel.getRemoteAddress().toString();
+        }
+        String dpidString;
+        if (featuresReply == null) {
+            dpidString = "?";
+        } else {
+            dpidString = featuresReply.getDatapathId().toString();
+        }
+        return String.format("[%s DPID[%s]]", channelString, dpidString);
+    }
+
+    /**
+     * Update the channels state. Only called from the state machine.
+     * TODO: enforce restricted state transitions
+     * @param state
+     */
+    private void setState(ChannelState state) {
+        this.state = state;
+    }
+
+    /**
+     * Send hello message to the switch using the handshake transactions ids.
+     * @throws IOException
+     */
+    private void sendHandshakeHelloMessage() throws IOException {
+        // The OF protocol requires us to start things off by sending the highest
+        // version of the protocol supported.
+
+        // bitmap represents OF1.0 (ofp_version=0x01) and OF1.3 (ofp_version=0x04)
+        // see Sec. 7.5.1 of the OF1.3.4 spec
+        U32 bitmap = U32.ofRaw(0x00000012);
+        OFHelloElem hem = factory13.buildHelloElemVersionbitmap()
+                .setBitmaps(Collections.singletonList(bitmap))
+                .build();
+        OFMessage.Builder mb = factory13.buildHello()
+                .setXid(this.handshakeTransactionIds--)
+                .setElements(Collections.singletonList(hem));
+        log.info("Sending OF_13 Hello to {}", channel.getRemoteAddress());
+        channel.write(Collections.singletonList(mb.build()));
+    }
+
+    /**
+     * Send featuresRequest msg to the switch using the handshake transactions ids.
+     * @throws IOException
+     */
+    private void sendHandshakeFeaturesRequestMessage() throws IOException {
+        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFMessage m = factory.buildFeaturesRequest()
+                .setXid(this.handshakeTransactionIds--)
+                .build();
+        channel.write(Collections.singletonList(m));
+    }
+
+    /**
+     * Send the configuration requests to tell the switch we want full
+     * packets.
+     * @throws IOException
+     */
+    private void sendHandshakeSetConfig() throws IOException {
+        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        //log.debug("Sending CONFIG_REQUEST to {}", channel.getRemoteAddress());
+        List<OFMessage> msglist = new ArrayList<OFMessage>(3);
+
+        // Ensure we receive the full packet via PacketIn
+        // FIXME: We don't set the reassembly flags.
+	// Only send config to switches to send full packets, if they have a buffer.
+        // Saves a packet & OFSetConfig can't be handled by certain switches.
+        if(this.featuresReply.getNBuffers() > 0) {
+            OFSetConfig sc = factory
+                    .buildSetConfig()
+                    .setMissSendLen((short) 0xffff)
+                    .setXid(this.handshakeTransactionIds--)
+                    .build();
+            msglist.add(sc);
+        }
+
+        // Barrier
+        OFBarrierRequest br = factory
+                .buildBarrierRequest()
+                .setXid(this.handshakeTransactionIds--)
+                .build();
+        msglist.add(br);
+
+        // Verify (need barrier?)
+        OFGetConfigRequest gcr = factory
+                .buildGetConfigRequest()
+                .setXid(this.handshakeTransactionIds--)
+                .build();
+        msglist.add(gcr);
+        channel.write(msglist);
+    }
+
+    /**
+     * send a description state request.
+     * @throws IOException
+     */
+    private void sendHandshakeDescriptionStatsRequest() throws IOException {
+        // Get Description to set switch-specific flags
+        OFFactory factory = (ofVersion == OFVersion.OF_13) ? factory13 : factory10;
+        OFDescStatsRequest dreq = factory
+                .buildDescStatsRequest()
+                .setXid(handshakeTransactionIds--)
+                .build();
+        channel.write(Collections.singletonList(dreq));
+    }
+
+    private void sendHandshakeOFPortDescRequest() throws IOException {
+        // Get port description for 1.3 switch
+        OFPortDescStatsRequest preq = factory13
+                .buildPortDescStatsRequest()
+                .setXid(handshakeTransactionIds--)
+                .build();
+        channel.write(Collections.singletonList(preq));
+    }
+
+    ChannelState getStateForTesting() {
+        return state;
+    }
+
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageDecoder.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageDecoder.java
new file mode 100644
index 0000000..f52d27e
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageDecoder.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.impl;
+
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.frame.FrameDecoder;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFMessageReader;
+
+/**
+ * Decode an openflow message from a Channel, for use in a netty pipeline.
+ */
+public class OFMessageDecoder extends FrameDecoder {
+
+    @Override
+    protected Object decode(ChannelHandlerContext ctx, Channel channel,
+            ChannelBuffer buffer) throws Exception {
+        if (!channel.isConnected()) {
+            // In testing, I see decode being called AFTER decode last.
+            // This check avoids that from reading corrupted frames
+            return null;
+        }
+
+        // Note that a single call to decode results in reading a single
+        // OFMessage from the channel buffer, which is passed on to, and processed
+        // by, the controller (in OFChannelHandler).
+        // This is different from earlier behavior (with the original openflowj),
+        // where we parsed all the messages in the buffer, before passing on
+        // a list of the parsed messages to the controller.
+        // The performance *may or may not* not be as good as before.
+        OFMessageReader<OFMessage> reader = OFFactories.getGenericReader();
+        OFMessage message = reader.readFrom(buffer);
+
+        return message;
+    }
+
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageEncoder.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageEncoder.java
new file mode 100644
index 0000000..4c1b16f
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageEncoder.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.impl;
+
+import java.util.List;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+
+/**
+ * Encode an openflow message for output into a ChannelBuffer, for use in a
+ * netty pipeline.
+ */
+public class OFMessageEncoder extends OneToOneEncoder {
+
+    @Override
+    protected Object encode(ChannelHandlerContext ctx, Channel channel,
+                            Object msg) throws Exception {
+        if (!(msg instanceof List)) {
+            return msg;
+        }
+
+        @SuppressWarnings("unchecked")
+        List<OFMessage> msglist = (List<OFMessage>) msg;
+        /* XXX S can't get length of OFMessage in loxigen's openflowj??
+        int size = 0;
+        for (OFMessage ofm : msglist) {
+            size += ofm.getLengthU();
+        }*/
+
+        ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
+
+        for (OFMessage ofm : msglist) {
+            if (ofm != null) {
+                ofm.writeTo(buf);
+            }
+        }
+        return buf;
+    }
+
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
new file mode 100644
index 0000000..b97c336
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java
@@ -0,0 +1,633 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.net.driver.DefaultDriverProviderService;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowPacketContext;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.PacketListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.onosproject.openflow.controller.driver.OpenFlowAgent;
+import org.osgi.service.component.ComponentContext;
+import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFCalientFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFCircuitPortStatus;
+import org.projectfloodlight.openflow.protocol.OFExperimenter;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFTableStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFTableStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import static org.onlab.util.Tools.groupedThreads;
+
+@Component(immediate = true)
+@Service
+public class OpenFlowControllerImpl implements OpenFlowController {
+    private static final String DEFAULT_OFPORT = "6633,6653";
+    private static final int DEFAULT_WORKER_THREADS = 16;
+
+    private static final Logger log =
+            LoggerFactory.getLogger(OpenFlowControllerImpl.class);
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DriverService driverService;
+
+    // References exists merely for sequencing purpose to assure drivers are loaded
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DefaultDriverProviderService defaultDriverProviderService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService cfgService;
+
+    @Property(name = "openflowPorts", value = DEFAULT_OFPORT,
+            label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653")
+    private String openflowPorts = DEFAULT_OFPORT;
+
+    @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS,
+            label = "Number of controller worker threads; default is 16")
+    private int workerThreads = DEFAULT_WORKER_THREADS;
+
+    protected ExecutorService executorMsgs =
+        Executors.newFixedThreadPool(32, groupedThreads("onos/of", "event-stats-%d"));
+
+    private final ExecutorService executorBarrier =
+        Executors.newFixedThreadPool(4, groupedThreads("onos/of", "event-barrier-%d"));
+
+    protected ConcurrentHashMap<Dpid, OpenFlowSwitch> connectedSwitches =
+            new ConcurrentHashMap<>();
+    protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeMasterSwitches =
+            new ConcurrentHashMap<>();
+    protected ConcurrentHashMap<Dpid, OpenFlowSwitch> activeEqualSwitches =
+            new ConcurrentHashMap<>();
+
+    protected OpenFlowSwitchAgent agent = new OpenFlowSwitchAgent();
+    protected Set<OpenFlowSwitchListener> ofSwitchListener = new CopyOnWriteArraySet<>();
+
+    protected Multimap<Integer, PacketListener> ofPacketListener =
+            ArrayListMultimap.create();
+
+    protected Set<OpenFlowEventListener> ofEventListener = new CopyOnWriteArraySet<>();
+
+    protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats =
+            ArrayListMultimap.create();
+
+    protected Multimap<Dpid, OFTableStatsEntry> fullTableStats =
+            ArrayListMultimap.create();
+
+    protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats =
+            ArrayListMultimap.create();
+
+    protected Multimap<Dpid, OFGroupDescStatsEntry> fullGroupDescStats =
+            ArrayListMultimap.create();
+
+    protected Multimap<Dpid, OFPortStatsEntry> fullPortStats =
+            ArrayListMultimap.create();
+
+    private final Controller ctrl = new Controller();
+
+    @Activate
+    public void activate(ComponentContext context) {
+        cfgService.registerProperties(getClass());
+        ctrl.setConfigParams(context.getProperties());
+        ctrl.start(agent, driverService);
+    }
+
+    @Deactivate
+    public void deactivate() {
+        cfgService.unregisterProperties(getClass(), false);
+        ctrl.stop();
+    }
+
+    @Modified
+    public void modified(ComponentContext context) {
+        ctrl.stop();
+        ctrl.setConfigParams(context.getProperties());
+        ctrl.start(agent, driverService);
+    }
+
+    @Override
+    public Iterable<OpenFlowSwitch> getSwitches() {
+        return connectedSwitches.values();
+    }
+
+    @Override
+    public Iterable<OpenFlowSwitch> getMasterSwitches() {
+        return activeMasterSwitches.values();
+    }
+
+    @Override
+    public Iterable<OpenFlowSwitch> getEqualSwitches() {
+        return activeEqualSwitches.values();
+    }
+
+    @Override
+    public OpenFlowSwitch getSwitch(Dpid dpid) {
+        return connectedSwitches.get(dpid);
+    }
+
+    @Override
+    public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
+        return activeMasterSwitches.get(dpid);
+    }
+
+    @Override
+    public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
+        return activeEqualSwitches.get(dpid);
+    }
+
+    @Override
+    public void addListener(OpenFlowSwitchListener listener) {
+        if (!ofSwitchListener.contains(listener)) {
+            this.ofSwitchListener.add(listener);
+        }
+    }
+
+    @Override
+    public void removeListener(OpenFlowSwitchListener listener) {
+        this.ofSwitchListener.remove(listener);
+    }
+
+    @Override
+    public void addPacketListener(int priority, PacketListener listener) {
+        ofPacketListener.put(priority, listener);
+    }
+
+    @Override
+    public void removePacketListener(PacketListener listener) {
+        ofPacketListener.values().remove(listener);
+    }
+
+    @Override
+    public void addEventListener(OpenFlowEventListener listener) {
+        ofEventListener.add(listener);
+    }
+
+    @Override
+    public void removeEventListener(OpenFlowEventListener listener) {
+        ofEventListener.remove(listener);
+    }
+
+    @Override
+    public void write(Dpid dpid, OFMessage msg) {
+        this.getSwitch(dpid).sendMsg(msg);
+    }
+
+    @Override
+    public void processPacket(Dpid dpid, OFMessage msg) {
+        Collection<OFFlowStatsEntry> flowStats;
+        Collection<OFTableStatsEntry> tableStats;
+        Collection<OFGroupStatsEntry> groupStats;
+        Collection<OFGroupDescStatsEntry> groupDescStats;
+        Collection<OFPortStatsEntry> portStats;
+
+        switch (msg.getType()) {
+        case PORT_STATUS:
+            for (OpenFlowSwitchListener l : ofSwitchListener) {
+                l.portChanged(dpid, (OFPortStatus) msg);
+            }
+            break;
+        case FEATURES_REPLY:
+            for (OpenFlowSwitchListener l : ofSwitchListener) {
+                l.switchChanged(dpid);
+            }
+            break;
+        case PACKET_IN:
+            OpenFlowPacketContext pktCtx = DefaultOpenFlowPacketContext
+            .packetContextFromPacketIn(this.getSwitch(dpid),
+                    (OFPacketIn) msg);
+            for (PacketListener p : ofPacketListener.values()) {
+                p.handlePacket(pktCtx);
+            }
+            break;
+        // TODO: Consider using separate threadpool for sensitive messages.
+        //    ie. Back to back error could cause us to starve.
+        case FLOW_REMOVED:
+        case ERROR:
+            executorMsgs.submit(new OFMessageHandler(dpid, msg));
+            break;
+        case STATS_REPLY:
+            OFStatsReply reply = (OFStatsReply) msg;
+            switch (reply.getStatsType()) {
+                case PORT_DESC:
+                    for (OpenFlowSwitchListener l : ofSwitchListener) {
+                        l.switchChanged(dpid);
+                    }
+                    break;
+                case FLOW:
+                    flowStats = publishFlowStats(dpid, (OFFlowStatsReply) reply);
+                    if (flowStats != null) {
+                        OFFlowStatsReply.Builder rep =
+                                OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
+                        rep.setEntries(Lists.newLinkedList(flowStats));
+                        rep.setXid(reply.getXid());
+                        executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
+                    }
+                    break;
+                case TABLE:
+                    tableStats = publishTableStats(dpid, (OFTableStatsReply) reply);
+                    if (tableStats != null) {
+                        OFTableStatsReply.Builder rep =
+                                OFFactories.getFactory(msg.getVersion()).buildTableStatsReply();
+                        rep.setEntries(Lists.newLinkedList(tableStats));
+                        executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
+                    }
+                    break;
+                case GROUP:
+                    groupStats = publishGroupStats(dpid, (OFGroupStatsReply) reply);
+                    if (groupStats != null) {
+                        OFGroupStatsReply.Builder rep =
+                                OFFactories.getFactory(msg.getVersion()).buildGroupStatsReply();
+                        rep.setEntries(Lists.newLinkedList(groupStats));
+                        rep.setXid(reply.getXid());
+                        executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
+                    }
+                    break;
+                case GROUP_DESC:
+                    groupDescStats = publishGroupDescStats(dpid,
+                            (OFGroupDescStatsReply) reply);
+                    if (groupDescStats != null) {
+                        OFGroupDescStatsReply.Builder rep =
+                                OFFactories.getFactory(msg.getVersion()).buildGroupDescStatsReply();
+                        rep.setEntries(Lists.newLinkedList(groupDescStats));
+                        rep.setXid(reply.getXid());
+                        executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
+                    }
+                    break;
+                case PORT:
+                    executorMsgs.submit(new OFMessageHandler(dpid, reply));
+                    break;
+                case METER:
+                    executorMsgs.submit(new OFMessageHandler(dpid, reply));
+                    break;
+                case EXPERIMENTER:
+                    if (reply instanceof OFCalientFlowStatsReply) {
+                        // Convert Calient flow statistics to regular flow stats
+                        // TODO: parse remaining fields such as power levels etc. when we have proper monitoring API
+                        OFFlowStatsReply.Builder fsr = getSwitch(dpid).factory().buildFlowStatsReply();
+                        List<OFFlowStatsEntry> entries = new LinkedList<>();
+                        for (OFCalientFlowStatsEntry entry : ((OFCalientFlowStatsReply) msg).getEntries()) {
+
+                            // Single instruction, i.e., output to port
+                            OFActionOutput action = OFFactories
+                                    .getFactory(msg.getVersion())
+                                    .actions()
+                                    .buildOutput()
+                                    .setPort(entry.getOutPort())
+                                    .build();
+                            OFInstruction instruction = OFFactories
+                                    .getFactory(msg.getVersion())
+                                    .instructions()
+                                    .applyActions(Collections.singletonList(action));
+                            OFFlowStatsEntry fs = getSwitch(dpid).factory().buildFlowStatsEntry()
+                                    .setMatch(entry.getMatch())
+                                    .setTableId(entry.getTableId())
+                                    .setDurationSec(entry.getDurationSec())
+                                    .setDurationNsec(entry.getDurationNsec())
+                                    .setPriority(entry.getPriority())
+                                    .setIdleTimeout(entry.getIdleTimeout())
+                                    .setHardTimeout(entry.getHardTimeout())
+                                    .setFlags(entry.getFlags())
+                                    .setCookie(entry.getCookie())
+                                    .setInstructions(Collections.singletonList(instruction))
+                                    .build();
+                            entries.add(fs);
+                        }
+                        fsr.setEntries(entries);
+
+                        flowStats = publishFlowStats(dpid, fsr.build());
+                        if (flowStats != null) {
+                            OFFlowStatsReply.Builder rep =
+                                    OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply();
+                            rep.setEntries(Lists.newLinkedList(flowStats));
+                            executorMsgs.submit(new OFMessageHandler(dpid, rep.build()));
+                        }
+                    } else {
+                        executorMsgs.submit(new OFMessageHandler(dpid, reply));
+                    }
+                    break;
+                default:
+                    log.warn("Discarding unknown stats reply type {}", reply.getStatsType());
+                    break;
+            }
+            break;
+        case BARRIER_REPLY:
+            executorBarrier.submit(new OFMessageHandler(dpid, msg));
+            break;
+        case EXPERIMENTER:
+            long experimenter = ((OFExperimenter) msg).getExperimenter();
+            if (experimenter == 0x748771) {
+                // LINC-OE port stats
+                OFCircuitPortStatus circuitPortStatus = (OFCircuitPortStatus) msg;
+                OFPortStatus.Builder portStatus = this.getSwitch(dpid).factory().buildPortStatus();
+                OFPortDesc.Builder portDesc = this.getSwitch(dpid).factory().buildPortDesc();
+                portDesc.setPortNo(circuitPortStatus.getPortNo())
+                        .setHwAddr(circuitPortStatus.getHwAddr())
+                        .setName(circuitPortStatus.getName())
+                        .setConfig(circuitPortStatus.getConfig())
+                        .setState(circuitPortStatus.getState());
+                portStatus.setReason(circuitPortStatus.getReason()).setDesc(portDesc.build());
+                for (OpenFlowSwitchListener l : ofSwitchListener) {
+                    l.portChanged(dpid, portStatus.build());
+                }
+            } else {
+                log.warn("Handling experimenter type {} not yet implemented",
+                        ((OFExperimenter) msg).getExperimenter(), msg);
+            }
+            break;
+        default:
+            log.warn("Handling message type {} not yet implemented {}",
+                    msg.getType(), msg);
+        }
+    }
+
+    private synchronized Collection<OFFlowStatsEntry> publishFlowStats(Dpid dpid,
+                                                                       OFFlowStatsReply reply) {
+        //TODO: Get rid of synchronized
+        fullFlowStats.putAll(dpid, reply.getEntries());
+        if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+            return fullFlowStats.removeAll(dpid);
+        }
+        return null;
+    }
+
+    private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid,
+                                                                       OFTableStatsReply reply) {
+        //TODO: Get rid of synchronized
+        fullTableStats.putAll(dpid, reply.getEntries());
+        if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+            return fullTableStats.removeAll(dpid);
+        }
+        return null;
+    }
+
+    private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid,
+                                                                      OFGroupStatsReply reply) {
+        //TODO: Get rid of synchronized
+        fullGroupStats.putAll(dpid, reply.getEntries());
+        if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+            return fullGroupStats.removeAll(dpid);
+        }
+        return null;
+    }
+
+    private synchronized Collection<OFGroupDescStatsEntry> publishGroupDescStats(Dpid dpid,
+                                                                  OFGroupDescStatsReply reply) {
+        //TODO: Get rid of synchronized
+        fullGroupDescStats.putAll(dpid, reply.getEntries());
+        if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+            return fullGroupDescStats.removeAll(dpid);
+        }
+        return null;
+    }
+
+    private synchronized Collection<OFPortStatsEntry> publishPortStats(Dpid dpid,
+                                                                 OFPortStatsReply reply) {
+        fullPortStats.putAll(dpid, reply.getEntries());
+        if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+            return fullPortStats.removeAll(dpid);
+        }
+        return null;
+    }
+
+    @Override
+    public void setRole(Dpid dpid, RoleState role) {
+        final OpenFlowSwitch sw = getSwitch(dpid);
+        if (sw == null) {
+            log.debug("Switch not connected. Ignoring setRole({}, {})", dpid, role);
+            return;
+        }
+        sw.setRole(role);
+    }
+
+    /**
+     * Implementation of an OpenFlow Agent which is responsible for
+     * keeping track of connected switches and the state in which
+     * they are.
+     */
+    public class OpenFlowSwitchAgent implements OpenFlowAgent {
+
+        private final Logger log = LoggerFactory.getLogger(OpenFlowSwitchAgent.class);
+        private final Lock switchLock = new ReentrantLock();
+
+        @Override
+        public boolean addConnectedSwitch(Dpid dpid, OpenFlowSwitch sw) {
+
+            if (connectedSwitches.get(dpid) != null) {
+                log.error("Trying to add connectedSwitch but found a previous "
+                        + "value for dpid: {}", dpid);
+                return false;
+            } else {
+                log.info("Added switch {}", dpid);
+                connectedSwitches.put(dpid, sw);
+                for (OpenFlowSwitchListener l : ofSwitchListener) {
+                    l.switchAdded(dpid);
+                }
+                return true;
+            }
+        }
+
+        @Override
+        public boolean validActivation(Dpid dpid) {
+            if (connectedSwitches.get(dpid) == null) {
+                log.error("Trying to activate switch but is not in "
+                        + "connected switches: dpid {}. Aborting ..",
+                        dpid);
+                return false;
+            }
+            if (activeMasterSwitches.get(dpid) != null ||
+                    activeEqualSwitches.get(dpid) != null) {
+                log.error("Trying to activate switch but it is already "
+                        + "activated: dpid {}. Found in activeMaster: {} "
+                        + "Found in activeEqual: {}. Aborting ..",
+                          dpid,
+                          (activeMasterSwitches.get(dpid) == null) ? 'N' : 'Y',
+                          (activeEqualSwitches.get(dpid) == null) ? 'N' : 'Y');
+                return false;
+            }
+            return true;
+        }
+
+
+        @Override
+        public boolean addActivatedMasterSwitch(Dpid dpid, OpenFlowSwitch sw) {
+            switchLock.lock();
+            try {
+                if (!validActivation(dpid)) {
+                    return false;
+                }
+                activeMasterSwitches.put(dpid, sw);
+                return true;
+            } finally {
+                switchLock.unlock();
+            }
+        }
+
+        @Override
+        public boolean addActivatedEqualSwitch(Dpid dpid, OpenFlowSwitch sw) {
+            switchLock.lock();
+            try {
+                if (!validActivation(dpid)) {
+                    return false;
+                }
+                activeEqualSwitches.put(dpid, sw);
+                log.info("Added Activated EQUAL Switch {}", dpid);
+                return true;
+            } finally {
+                switchLock.unlock();
+            }
+        }
+
+        @Override
+        public void transitionToMasterSwitch(Dpid dpid) {
+            switchLock.lock();
+            try {
+                if (activeMasterSwitches.containsKey(dpid)) {
+                    return;
+                }
+                OpenFlowSwitch sw = activeEqualSwitches.remove(dpid);
+                if (sw == null) {
+                    sw = getSwitch(dpid);
+                    if (sw == null) {
+                        log.error("Transition to master called on sw {}, but switch "
+                                + "was not found in controller-cache", dpid);
+                        return;
+                    }
+                }
+                log.info("Transitioned switch {} to MASTER", dpid);
+                activeMasterSwitches.put(dpid, sw);
+            } finally {
+                switchLock.unlock();
+            }
+        }
+
+
+        @Override
+        public void transitionToEqualSwitch(Dpid dpid) {
+            switchLock.lock();
+            try {
+                if (activeEqualSwitches.containsKey(dpid)) {
+                    return;
+                }
+                OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
+                if (sw == null) {
+                    sw = getSwitch(dpid);
+                    if (sw == null) {
+                        log.error("Transition to equal called on sw {}, but switch "
+                                + "was not found in controller-cache", dpid);
+                        return;
+                    }
+                }
+                log.info("Transitioned switch {} to EQUAL", dpid);
+                activeEqualSwitches.put(dpid, sw);
+            } finally {
+                switchLock.unlock();
+            }
+
+        }
+
+        @Override
+        public void removeConnectedSwitch(Dpid dpid) {
+            connectedSwitches.remove(dpid);
+            OpenFlowSwitch sw = activeMasterSwitches.remove(dpid);
+            if (sw == null) {
+                log.debug("sw was null for {}", dpid);
+                sw = activeEqualSwitches.remove(dpid);
+            }
+            for (OpenFlowSwitchListener l : ofSwitchListener) {
+                l.switchRemoved(dpid);
+            }
+        }
+
+        @Override
+        public void processMessage(Dpid dpid, OFMessage m) {
+            processPacket(dpid, m);
+        }
+
+        @Override
+        public void returnRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+            for (OpenFlowSwitchListener l : ofSwitchListener) {
+                l.receivedRoleReply(dpid, requested, response);
+            }
+        }
+    }
+
+    protected final class OFMessageHandler implements Runnable {
+
+        protected final OFMessage msg;
+        protected final Dpid dpid;
+
+        public OFMessageHandler(Dpid dpid, OFMessage msg) {
+            this.msg = msg;
+            this.dpid = dpid;
+        }
+
+        @Override
+        public void run() {
+            for (OpenFlowEventListener listener : ofEventListener) {
+                listener.handleMessage(dpid, msg);
+            }
+        }
+
+    }
+
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java
new file mode 100644
index 0000000..1467520
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.openflow.controller.impl;
+
+import java.util.concurrent.ThreadPoolExecutor;
+
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.Channels;
+import org.jboss.netty.handler.execution.ExecutionHandler;
+import org.jboss.netty.handler.timeout.IdleStateHandler;
+import org.jboss.netty.handler.timeout.ReadTimeoutHandler;
+import org.jboss.netty.util.ExternalResourceReleasable;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Creates a ChannelPipeline for a server-side openflow channel.
+ */
+public class OpenflowPipelineFactory
+    implements ChannelPipelineFactory, ExternalResourceReleasable {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final SSLEngine sslEngine;
+    protected Controller controller;
+    protected ThreadPoolExecutor pipelineExecutor;
+    protected Timer timer;
+    protected IdleStateHandler idleHandler;
+    protected ReadTimeoutHandler readTimeoutHandler;
+
+    public OpenflowPipelineFactory(Controller controller,
+                                   ThreadPoolExecutor pipelineExecutor,
+                                   SSLEngine sslEngine) {
+        super();
+        this.controller = controller;
+        this.pipelineExecutor = pipelineExecutor;
+        this.timer = new HashedWheelTimer();
+        this.idleHandler = new IdleStateHandler(timer, 20, 25, 0);
+        this.readTimeoutHandler = new ReadTimeoutHandler(timer, 30);
+        this.sslEngine = sslEngine;
+    }
+
+    @Override
+    public ChannelPipeline getPipeline() throws Exception {
+        OFChannelHandler handler = new OFChannelHandler(controller);
+
+        ChannelPipeline pipeline = Channels.pipeline();
+        if (sslEngine != null) {
+            log.info("OpenFlow SSL enabled.");
+            pipeline.addLast("ssl",
+                             new org.jboss.netty.handler.ssl.SslHandler(sslEngine));
+        } else {
+            log.info("OpenFlow SSL disabled");
+        }
+        pipeline.addLast("ofmessagedecoder", new OFMessageDecoder());
+        pipeline.addLast("ofmessageencoder", new OFMessageEncoder());
+        pipeline.addLast("idle", idleHandler);
+        pipeline.addLast("timeout", readTimeoutHandler);
+        // XXX S ONOS: was 15 increased it to fix Issue #296
+        pipeline.addLast("handshaketimeout",
+                         new HandshakeTimeoutHandler(handler, timer, 60));
+        if (pipelineExecutor != null) {
+            pipeline.addLast("pipelineExecutor",
+                             new ExecutionHandler(pipelineExecutor));
+        }
+        pipeline.addLast("handler", handler);
+        return pipeline;
+    }
+
+    @Override
+    public void releaseExternalResources() {
+        timer.stop();
+    }
+}
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/RoleManager.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/RoleManager.java
new file mode 100644
index 0000000..bd4875c
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/RoleManager.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.onosproject.openflow.controller.RoleState;
+import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
+import org.onosproject.openflow.controller.driver.RoleHandler;
+import org.onosproject.openflow.controller.driver.RoleRecvStatus;
+import org.onosproject.openflow.controller.driver.RoleReplyInfo;
+import org.onosproject.openflow.controller.driver.SwitchStateException;
+import org.projectfloodlight.openflow.protocol.OFControllerRole;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFErrorType;
+import org.projectfloodlight.openflow.protocol.OFExperimenter;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFNiciraControllerRole;
+import org.projectfloodlight.openflow.protocol.OFNiciraControllerRoleReply;
+import org.projectfloodlight.openflow.protocol.OFRoleReply;
+import org.projectfloodlight.openflow.protocol.OFRoleRequest;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
+import org.projectfloodlight.openflow.protocol.errormsg.OFRoleRequestFailedErrorMsg;
+import org.projectfloodlight.openflow.types.U64;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * A utility class to handle role requests and replies for this channel.
+ * After a role request is submitted the role changer keeps track of the
+ * pending request, collects the reply (if any) and times out the request
+ * if necessary.
+ */
+class RoleManager implements RoleHandler {
+    protected static final long NICIRA_EXPERIMENTER = 0x2320;
+
+    private static Logger log = LoggerFactory.getLogger(RoleManager.class);
+
+    // The time until cached XID is evicted. Arbitrary for now.
+    private final int pendingXidTimeoutSeconds = 60;
+
+    // The cache for pending expected RoleReplies keyed on expected XID
+    private Cache<Integer, RoleState> pendingReplies =
+            CacheBuilder.newBuilder()
+                .expireAfterWrite(pendingXidTimeoutSeconds, TimeUnit.SECONDS)
+                .build();
+
+    // the expectation set by the caller for the returned role
+    private RoleRecvStatus expectation;
+    private final OpenFlowSwitchDriver sw;
+
+
+    public RoleManager(OpenFlowSwitchDriver sw) {
+        this.expectation = RoleRecvStatus.MATCHED_CURRENT_ROLE;
+        this.sw = sw;
+    }
+
+    /**
+     * Send NX role request message to the switch requesting the specified
+     * role.
+     *
+     * @param role role to request
+     */
+    private int sendNxRoleRequest(RoleState role) throws IOException {
+        // Convert the role enum to the appropriate role to send
+        OFNiciraControllerRole roleToSend = OFNiciraControllerRole.ROLE_OTHER;
+        switch (role) {
+        case MASTER:
+            roleToSend = OFNiciraControllerRole.ROLE_MASTER;
+            break;
+        case SLAVE:
+        case EQUAL:
+        default:
+            // ensuring that the only two roles sent to 1.0 switches with
+            // Nicira role support, are MASTER and SLAVE
+            roleToSend = OFNiciraControllerRole.ROLE_OTHER;
+            log.debug("Sending Nx Role.SLAVE to switch {}.", sw);
+        }
+        int xid = sw.getNextTransactionId();
+        OFExperimenter roleRequest = OFFactories.getFactory(OFVersion.OF_10)
+                .buildNiciraControllerRoleRequest()
+                .setXid(xid)
+                .setRole(roleToSend)
+                .build();
+        sw.sendRoleRequest(roleRequest);
+        return xid;
+    }
+
+    private int sendOF13RoleRequest(RoleState role) throws IOException {
+        // Convert the role enum to the appropriate role to send
+        OFControllerRole roleToSend = OFControllerRole.ROLE_NOCHANGE;
+        switch (role) {
+        case EQUAL:
+            roleToSend = OFControllerRole.ROLE_EQUAL;
+            break;
+        case MASTER:
+            roleToSend = OFControllerRole.ROLE_MASTER;
+            break;
+        case SLAVE:
+            roleToSend = OFControllerRole.ROLE_SLAVE;
+            break;
+        default:
+            log.warn("Sending default role.noChange to switch {}."
+                    + " Should only be used for queries.", sw);
+        }
+
+        int xid = sw.getNextTransactionId();
+        OFRoleRequest rrm = OFFactories.getFactory(OFVersion.OF_13)
+                .buildRoleRequest()
+                .setRole(roleToSend)
+                .setXid(xid)
+                //FIXME fix below when we actually use generation ids
+                .setGenerationId(U64.ZERO)
+                .build();
+
+        sw.sendRoleRequest(rrm);
+        return xid;
+    }
+
+    @Override
+    public synchronized boolean sendRoleRequest(RoleState role, RoleRecvStatus exp)
+            throws IOException {
+        this.expectation = exp;
+
+        if (sw.factory().getVersion() == OFVersion.OF_10) {
+            Boolean supportsNxRole = sw.supportNxRole();
+            if (!supportsNxRole) {
+                log.debug("Switch driver indicates no support for Nicira "
+                        + "role request messages. Not sending ...");
+                handleUnsentRoleMessage(role,
+                        expectation);
+                return false;
+            }
+            // OF1.0 switch with support for NX_ROLE_REQUEST vendor extn.
+            // make Role.EQUAL become Role.SLAVE
+            RoleState roleToSend = (role == RoleState.EQUAL) ? RoleState.SLAVE : role;
+            pendingReplies.put(sendNxRoleRequest(roleToSend), role);
+        } else {
+            // OF1.3 switch, use OFPT_ROLE_REQUEST message
+            pendingReplies.put(sendOF13RoleRequest(role), role);
+        }
+        return true;
+    }
+
+    private void handleUnsentRoleMessage(RoleState role,
+            RoleRecvStatus exp) throws IOException {
+        // typically this is triggered for a switch where role messages
+        // are not supported - we confirm that the role being set is
+        // master
+        if (exp != RoleRecvStatus.MATCHED_SET_ROLE) {
+
+            log.error("Expected MASTER role from registry for switch "
+                    + "which has no support for role-messages."
+                    + "Received {}. It is possible that this switch "
+                    + "is connected to other controllers, in which "
+                    + "case it should support role messages - not "
+                    + "moving forward.", role);
+
+        }
+
+    }
+
+
+    @Override
+    public synchronized RoleRecvStatus deliverRoleReply(RoleReplyInfo rri)
+            throws SwitchStateException {
+        int xid = (int) rri.getXid();
+        RoleState receivedRole = rri.getRole();
+        RoleState expectedRole = pendingReplies.getIfPresent(xid);
+
+        if (expectedRole == null) {
+            RoleState currentRole = (sw != null) ? sw.getRole() : null;
+            if (currentRole != null) {
+                if (currentRole == rri.getRole()) {
+                    // Don't disconnect if the role reply we received is
+                    // for the same role we are already in.
+                    // FIXME: but we do from the caller anyways.
+                    log.debug("Received unexpected RoleReply from "
+                            + "Switch: {}. "
+                            + "Role in reply is same as current role of this "
+                            + "controller for this sw. Ignoring ...",
+                            sw.getStringId());
+                    return RoleRecvStatus.OTHER_EXPECTATION;
+                } else {
+                    String msg = String.format("Switch: [%s], "
+                            + "received unexpected RoleReply[%s]. "
+                            + "No roles are pending, and this controller's "
+                            + "current role:[%s] does not match reply. "
+                            + "Disconnecting switch ... ",
+                            sw.getStringId(),
+                            rri, currentRole);
+                    throw new SwitchStateException(msg);
+                }
+            }
+            log.debug("Received unexpected RoleReply {} from "
+                    + "Switch: {}. "
+                    + "This controller has no current role for this sw. "
+                    + "Ignoring ...",
+                      rri,
+                      sw == null ? "(null)" : sw.getStringId());
+            return RoleRecvStatus.OTHER_EXPECTATION;
+        }
+
+        // XXX Should check generation id meaningfully and other cases of expectations
+        //if (pendingXid != xid) {
+        //    log.info("Received older role reply from " +
+        //            "switch {} ({}). Ignoring. " +
+        //            "Waiting for {}, xid={}",
+        //            new Object[] {sw.getStringId(), rri,
+        //            pendingRole, pendingXid });
+        //    return RoleRecvStatus.OLD_REPLY;
+        //}
+        sw.returnRoleReply(expectedRole, receivedRole);
+
+        if (expectedRole == receivedRole) {
+            log.debug("Received role reply message from {} that matched "
+                    + "expected role-reply {} with expectations {}",
+                    sw.getStringId(), receivedRole, expectation);
+
+            // Done with this RoleReply; Invalidate
+            pendingReplies.invalidate(xid);
+            if (expectation == RoleRecvStatus.MATCHED_CURRENT_ROLE ||
+                    expectation == RoleRecvStatus.MATCHED_SET_ROLE) {
+                return expectation;
+            } else {
+                return RoleRecvStatus.OTHER_EXPECTATION;
+            }
+        }
+
+        pendingReplies.invalidate(xid);
+        // if xids match but role's don't, perhaps its a query (OF1.3)
+        if (expectation == RoleRecvStatus.REPLY_QUERY) {
+            return expectation;
+        }
+
+        return RoleRecvStatus.OTHER_EXPECTATION;
+    }
+
+    /**
+     * Called if we receive an  error message. If the xid matches the
+     * pending request we handle it otherwise we ignore it.
+     *
+     * Note: since we only keep the last pending request we might get
+     * error messages for earlier role requests that we won't be able
+     * to handle
+     */
+    @Override
+    public synchronized RoleRecvStatus deliverError(OFErrorMsg error)
+            throws SwitchStateException {
+        RoleState errorRole = pendingReplies.getIfPresent(error.getXid());
+        if (errorRole == null) {
+            if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
+                log.debug("Received an error msg from sw {} for a role request,"
+                        + " but not for pending request in role-changer; "
+                        + " ignoring error {} ...",
+                        sw.getStringId(), error);
+            } else {
+                log.debug("Received an error msg from sw {}, but no pending "
+                        + "requests in role-changer; not handling ...",
+                        sw.getStringId());
+            }
+            return RoleRecvStatus.OTHER_EXPECTATION;
+        }
+        // it is an error related to a currently pending role request message
+        if (error.getErrType() == OFErrorType.BAD_REQUEST) {
+            log.error("Received a error msg {} from sw {} for "
+                    + "pending role request {}. Switch driver indicates "
+                    + "role-messaging is supported. Possible issues in "
+                    + "switch driver configuration?",
+                    ((OFBadRequestErrorMsg) error).toString(),
+                    sw.getStringId(),
+                    errorRole);
+            return RoleRecvStatus.UNSUPPORTED;
+        }
+
+        if (error.getErrType() == OFErrorType.ROLE_REQUEST_FAILED) {
+            OFRoleRequestFailedErrorMsg rrerr =
+                    (OFRoleRequestFailedErrorMsg) error;
+            switch (rrerr.getCode()) {
+            case BAD_ROLE:
+                // switch says that current-role-req has bad role?
+                // for now we disconnect
+                // fall-thru
+            case STALE:
+                // switch says that current-role-req has stale gen-id?
+                // for now we disconnect
+                // fall-thru
+            case UNSUP:
+                // switch says that current-role-req has role that
+                // cannot be supported? for now we disconnect
+                String msgx = String.format("Switch: [%s], "
+                        + "received Error to for pending role request [%s]. "
+                        + "Error:[%s]. Disconnecting switch ... ",
+                        sw.getStringId(),
+                        errorRole, rrerr);
+                throw new SwitchStateException(msgx);
+            default:
+                break;
+            }
+        }
+
+        // This error message was for a role request message but we dont know
+        // how to handle errors for nicira role request messages
+        return RoleRecvStatus.OTHER_EXPECTATION;
+    }
+
+    /**
+     * Extract the role from an OFVendor message.
+     *
+     * Extract the role from an OFVendor message if the message is a
+     * Nicira role reply. Otherwise return null.
+     *
+     * @param experimenterMsg message
+     * @return The role in the message if the message is a Nicira role
+     * reply, null otherwise.
+     * @throws SwitchStateException If the message is a Nicira role reply
+     * but the numeric role value is unknown.
+     */
+    @Override
+    public RoleState extractNiciraRoleReply(OFExperimenter experimenterMsg)
+            throws SwitchStateException {
+        int vendor = (int) experimenterMsg.getExperimenter();
+        if (vendor != 0x2320) {
+            return null;
+        }
+        OFNiciraControllerRoleReply nrr =
+                (OFNiciraControllerRoleReply) experimenterMsg;
+
+        RoleState role = null;
+        OFNiciraControllerRole ncr = nrr.getRole();
+        switch (ncr) {
+        case ROLE_MASTER:
+            role = RoleState.MASTER;
+            break;
+        case ROLE_OTHER:
+            role = RoleState.EQUAL;
+            break;
+        case ROLE_SLAVE:
+            role = RoleState.SLAVE;
+            break;
+        default: //handled below
+        }
+
+        if (role == null) {
+            String msg = String.format("Switch: [%s], "
+                    + "received NX_ROLE_REPLY with invalid role "
+                    + "value %s",
+                    sw.getStringId(),
+                    nrr.getRole());
+            throw new SwitchStateException(msg);
+        }
+        return role;
+    }
+
+    /**
+     * Extract the role information from an OF1.3 Role Reply Message.
+     *
+     * @param rrmsg the role message
+     * @return RoleReplyInfo object
+     * @throws SwitchStateException if the role information could not be extracted.
+     */
+    @Override
+    public RoleReplyInfo extractOFRoleReply(OFRoleReply rrmsg)
+            throws SwitchStateException {
+        OFControllerRole cr = rrmsg.getRole();
+        RoleState role = null;
+        switch (cr) {
+        case ROLE_EQUAL:
+            role = RoleState.EQUAL;
+            break;
+        case ROLE_MASTER:
+            role = RoleState.MASTER;
+            break;
+        case ROLE_SLAVE:
+            role = RoleState.SLAVE;
+            break;
+        case ROLE_NOCHANGE: // switch should send current role
+        default:
+            String msg = String.format("Unknown controller role %s "
+                    + "received from switch %s", cr, sw);
+            throw new SwitchStateException(msg);
+        }
+
+        return new RoleReplyInfo(role, rrmsg.getGenerationId(), rrmsg.getXid());
+    }
+
+}
+
diff --git a/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/package-info.java b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/package-info.java
new file mode 100644
index 0000000..a5d9f27
--- /dev/null
+++ b/protocols/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the OpenFlow controller IO subsystem.
+ */
+package org.onosproject.openflow.controller.impl;
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ChannelAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ChannelAdapter.java
new file mode 100644
index 0000000..75260a1
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ChannelAdapter.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.net.SocketAddress;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelConfig;
+import org.jboss.netty.channel.ChannelFactory;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelPipeline;
+
+/**
+ * Adapter for testing against a netty channel.
+ */
+public class ChannelAdapter implements Channel {
+    @Override
+    public Integer getId() {
+        return null;
+    }
+
+    @Override
+    public ChannelFactory getFactory() {
+        return null;
+    }
+
+    @Override
+    public Channel getParent() {
+        return null;
+    }
+
+    @Override
+    public ChannelConfig getConfig() {
+        return null;
+    }
+
+    @Override
+    public ChannelPipeline getPipeline() {
+        return null;
+    }
+
+    @Override
+    public boolean isOpen() {
+        return false;
+    }
+
+    @Override
+    public boolean isBound() {
+        return false;
+    }
+
+    @Override
+    public boolean isConnected() {
+        return false;
+    }
+
+    @Override
+    public SocketAddress getLocalAddress() {
+        return null;
+    }
+
+    @Override
+    public SocketAddress getRemoteAddress() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture write(Object o) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture write(Object o, SocketAddress socketAddress) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture bind(SocketAddress socketAddress) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture connect(SocketAddress socketAddress) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture disconnect() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture unbind() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture close() {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture getCloseFuture() {
+        return null;
+    }
+
+    @Override
+    public int getInterestOps() {
+        return 0;
+    }
+
+    @Override
+    public boolean isReadable() {
+        return false;
+    }
+
+    @Override
+    public boolean isWritable() {
+        return false;
+    }
+
+    @Override
+    public ChannelFuture setInterestOps(int i) {
+        return null;
+    }
+
+    @Override
+    public ChannelFuture setReadable(boolean b) {
+        return null;
+    }
+
+    @Override
+    public Object getAttachment() {
+        return null;
+    }
+
+    @Override
+    public void setAttachment(Object o) {
+
+    }
+
+    @Override
+    public int compareTo(Channel o) {
+        return 0;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ChannelHandlerContextAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ChannelHandlerContextAdapter.java
new file mode 100644
index 0000000..5b6c2a3
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ChannelHandlerContextAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelEvent;
+import org.jboss.netty.channel.ChannelHandler;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelPipeline;
+
+/**
+ * Adapter for testing against a netty channel handler context.
+ */
+public class ChannelHandlerContextAdapter implements ChannelHandlerContext {
+    @Override
+    public Channel getChannel() {
+        return null;
+    }
+
+    @Override
+    public ChannelPipeline getPipeline() {
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return null;
+    }
+
+    @Override
+    public ChannelHandler getHandler() {
+        return null;
+    }
+
+    @Override
+    public boolean canHandleUpstream() {
+        return false;
+    }
+
+    @Override
+    public boolean canHandleDownstream() {
+        return false;
+    }
+
+    @Override
+    public void sendUpstream(ChannelEvent channelEvent) {
+
+    }
+
+    @Override
+    public void sendDownstream(ChannelEvent channelEvent) {
+
+    }
+
+    @Override
+    public Object getAttachment() {
+        return null;
+    }
+
+    @Override
+    public void setAttachment(Object o) {
+
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/DriverAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/DriverAdapter.java
new file mode 100644
index 0000000..57becf9
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/DriverAdapter.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
+
+/**
+ * Created by ray on 11/4/15.
+ */
+public class DriverAdapter implements Driver {
+    @Override
+    public String name() {
+        return null;
+    }
+
+    @Override
+    public Driver parent() {
+        return null;
+    }
+
+    @Override
+    public String manufacturer() {
+        return null;
+    }
+
+    @Override
+    public String hwVersion() {
+        return null;
+    }
+
+    @Override
+    public String swVersion() {
+        return null;
+    }
+
+    @Override
+    public Set<Class<? extends Behaviour>> behaviours() {
+        return null;
+    }
+
+    @Override
+    public Class<? extends Behaviour> implementation(Class<? extends Behaviour> behaviour) {
+        return null;
+    }
+
+    @Override
+    public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
+        return true;
+    }
+
+    @Override
+    public <T extends Behaviour> T createBehaviour(DriverData data, Class<T> behaviourClass) {
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public <T extends Behaviour> T createBehaviour(DriverHandler handler, Class<T> behaviourClass) {
+        if (behaviourClass == OpenFlowSwitchDriver.class) {
+            return (T) new OpenflowSwitchDriverAdapter();
+        }
+        return null;
+    }
+
+    @Override
+    public Map<String, String> properties() {
+        return null;
+    }
+
+    @Override
+    public Driver merge(Driver other) {
+        return null;
+    }
+
+    @Override
+    public Set<String> keys() {
+        return null;
+    }
+
+    @Override
+    public String value(String key) {
+        return null;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/DriverServiceAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/DriverServiceAdapter.java
new file mode 100644
index 0000000..25596ad
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/DriverServiceAdapter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.util.Set;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+
+/**
+ * Created by ray on 11/4/15.
+ */
+public class DriverServiceAdapter implements DriverService {
+    @Override
+    public Set<Driver> getDrivers() {
+        return null;
+    }
+
+    @Override
+    public Set<Driver> getDrivers(Class<? extends Behaviour> withBehaviour) {
+        return null;
+    }
+
+    @Override
+    public Driver getDriver(String mfr, String hw, String sw) {
+        return null;
+    }
+
+    @Override
+    public Driver getDriver(DeviceId deviceId) {
+        return null;
+    }
+
+    @Override
+    public DriverHandler createHandler(DeviceId deviceId, String... credentials) {
+        return null;
+    }
+
+    @Override
+    public Driver getDriver(String driverName) {
+        return null;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ExecutorServiceAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ExecutorServiceAdapter.java
new file mode 100644
index 0000000..54c9c94
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/ExecutorServiceAdapter.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Test harness adapter for the ExecutorService.
+ */
+public class ExecutorServiceAdapter  implements ExecutorService {
+    @Override
+    public void shutdown() {
+
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        return null;
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return false;
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return false;
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit)
+            throws InterruptedException {
+        return false;
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+        return null;
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+        return null;
+    }
+
+    @Override
+    public Future<?> submit(Runnable task) {
+        return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException {
+        return null;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException {
+        return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
+        return null;
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        return null;
+    }
+
+    @Override
+    public void execute(Runnable command) {
+
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfFeaturesReply.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfFeaturesReply.java
new file mode 100644
index 0000000..e280d56
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfFeaturesReply.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.util.List;
+import java.util.Set;
+
+import org.projectfloodlight.openflow.protocol.OFActionType;
+import org.projectfloodlight.openflow.protocol.OFCapabilities;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.types.DatapathId;
+import org.projectfloodlight.openflow.types.OFAuxId;
+
+/**
+ * Mock of the Open FLow features reply message.
+ */
+public class MockOfFeaturesReply extends OfMessageAdapter implements OFFeaturesReply {
+    public MockOfFeaturesReply() {
+        super(OFType.FEATURES_REPLY);
+    }
+
+    @Override
+    public DatapathId getDatapathId() {
+        return null;
+    }
+
+    @Override
+    public long getNBuffers() {
+        return 0;
+    }
+
+    @Override
+    public short getNTables() {
+        return 0;
+    }
+
+    @Override
+    public Set<OFCapabilities> getCapabilities() {
+        return null;
+    }
+
+    @Override
+    public long getReserved() {
+        return 0;
+    }
+
+    @Override
+    public List<OFPortDesc> getPorts() {
+        return null;
+    }
+
+    @Override
+    public Set<OFActionType> getActions() {
+        return null;
+    }
+
+    @Override
+    public OFAuxId getAuxiliaryId() {
+        return null;
+    }
+
+    @Override
+    public OFFeaturesReply.Builder createBuilder() {
+        return null;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPacketIn.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPacketIn.java
new file mode 100644
index 0000000..8e2069b
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPacketIn.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPacketInReason;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U64;
+
+/**
+ * Mock of the Open Flow packet in message.
+ */
+public class MockOfPacketIn extends OfMessageAdapter implements OFPacketIn {
+    public MockOfPacketIn() {
+        super(OFType.PACKET_IN);
+    }
+
+    @Override
+    public OFBufferId getBufferId() {
+        return null;
+    }
+
+    @Override
+    public int getTotalLen() {
+        return 0;
+    }
+
+    @Override
+    public OFPacketInReason getReason() {
+        return null;
+    }
+
+    @Override
+    public TableId getTableId() {
+        return null;
+    }
+
+    @Override
+    public Match getMatch() {
+        return null;
+    }
+
+    @Override
+    public byte[] getData() {
+        return new byte[0];
+    }
+
+    @Override
+    public OFPort getInPort() {
+        return null;
+    }
+
+    @Override
+    public OFPort getInPhyPort() {
+        return null;
+    }
+
+    @Override
+    public U64 getCookie() {
+        return null;
+    }
+
+    @Override
+    public OFPacketIn.Builder createBuilder() {
+        return null;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPortStatus.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPortStatus.java
new file mode 100644
index 0000000..2e26542
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/MockOfPortStatus.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortReason;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFType;
+
+/**
+ * Mocked open flow port status message.
+ */
+public class MockOfPortStatus extends OfMessageAdapter implements OFPortStatus {
+    public MockOfPortStatus() {
+        super(OFType.PORT_STATUS);
+    }
+
+    @Override
+    public OFPortReason getReason() {
+        return null;
+    }
+
+    @Override
+    public OFPortDesc getDesc() {
+        return null;
+    }
+
+    @Override
+    public OFPortStatus.Builder createBuilder() {
+        return null;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OFDescStatsReplyAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OFDescStatsReplyAdapter.java
new file mode 100644
index 0000000..1e86641
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OFDescStatsReplyAdapter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.util.Set;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+import com.google.common.hash.PrimitiveSink;
+
+/**
+ * Created by ray on 11/4/15.
+ */
+public class OFDescStatsReplyAdapter implements OFDescStatsReply {
+    @Override
+    public OFVersion getVersion() {
+        return null;
+    }
+
+    @Override
+    public OFType getType() {
+        return null;
+    }
+
+    @Override
+    public long getXid() {
+        return 0;
+    }
+
+    @Override
+    public OFStatsType getStatsType() {
+        return null;
+    }
+
+    @Override
+    public Set<OFStatsReplyFlags> getFlags() {
+        return null;
+    }
+
+    @Override
+    public String getMfrDesc() {
+        return null;
+    }
+
+    @Override
+    public String getHwDesc() {
+        return null;
+    }
+
+    @Override
+    public String getSwDesc() {
+        return null;
+    }
+
+    @Override
+    public String getSerialNum() {
+        return null;
+    }
+
+    @Override
+    public String getDpDesc() {
+        return null;
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer channelBuffer) {
+
+    }
+
+    @Override
+    public Builder createBuilder() {
+        return null;
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OfMessageAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OfMessageAdapter.java
new file mode 100644
index 0000000..b7446eb
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OfMessageAdapter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+import com.google.common.hash.PrimitiveSink;
+
+/**
+ * Adapter for testing against an OpenFlow message.
+ */
+public class OfMessageAdapter implements OFMessage {
+    OFType type;
+
+    private OfMessageAdapter() {}
+
+    public OfMessageAdapter(OFType type) {
+        this.type = type;
+    }
+
+    @Override
+    public OFType getType() {
+        return type;
+    }
+
+    @Override
+    public OFVersion getVersion() {
+        return null;
+    }
+
+    @Override
+    public long getXid() {
+        return 0;
+    }
+
+    @Override
+    public void writeTo(ChannelBuffer channelBuffer) { }
+
+    @Override
+    public Builder createBuilder() {
+        return null;
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) { }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenFlowSwitchListenerAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenFlowSwitchListenerAdapter.java
new file mode 100644
index 0000000..b018f42
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenFlowSwitchListenerAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+
+/**
+ * Test harness for a switch listener.
+ */
+public class OpenFlowSwitchListenerAdapter implements OpenFlowSwitchListener {
+    final List<Dpid> removedDpids = new ArrayList<>();
+    final List<Dpid> addedDpids = new ArrayList<>();
+    final List<Dpid> changedDpids = new ArrayList<>();
+    final Map<Dpid, OFPortStatus> portChangedDpids = new HashMap<>();
+
+    @Override
+    public void switchAdded(Dpid dpid) {
+        addedDpids.add(dpid);
+    }
+
+    @Override
+    public void switchRemoved(Dpid dpid) {
+        removedDpids.add(dpid);
+    }
+
+    @Override
+    public void switchChanged(Dpid dpid) {
+        changedDpids.add(dpid);
+    }
+
+    @Override
+    public void portChanged(Dpid dpid, OFPortStatus status) {
+        portChangedDpids.put(dpid, status);
+    }
+
+    @Override
+    public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+        // Stub
+    }
+
+    public List<Dpid> removedDpids() {
+        return removedDpids;
+    }
+
+    public List<Dpid> addedDpids() {
+        return addedDpids;
+    }
+
+    public List<Dpid> changedDpids() {
+        return changedDpids;
+    }
+
+    public Map<Dpid, OFPortStatus> portChangedDpids() {
+        return portChangedDpids;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenflowSwitchDriverAdapter.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenflowSwitchDriverAdapter.java
new file mode 100644
index 0000000..9b899a6
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/OpenflowSwitchDriverAdapter.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow;
+
+import java.util.List;
+
+import org.jboss.netty.channel.Channel;
+import org.onosproject.net.Device;
+import org.onosproject.net.driver.DriverData;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.RoleState;
+import org.onosproject.openflow.controller.driver.OpenFlowAgent;
+import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
+import org.onosproject.openflow.controller.driver.RoleHandler;
+import org.onosproject.openflow.controller.driver.SwitchStateException;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+
+/**
+ * Testing adapter for the OpenFlow switch driver class.
+ */
+public class OpenflowSwitchDriverAdapter implements OpenFlowSwitchDriver {
+
+    RoleState role = RoleState.MASTER;
+
+    @Override
+    public void setAgent(OpenFlowAgent agent) {
+
+    }
+
+    @Override
+    public void setRoleHandler(RoleHandler roleHandler) {
+
+    }
+
+    @Override
+    public void reassertRole() {
+
+    }
+
+    @Override
+    public boolean handleRoleError(OFErrorMsg error) {
+        return false;
+    }
+
+    @Override
+    public void handleNiciraRole(OFMessage m) throws SwitchStateException {
+
+    }
+
+    @Override
+    public void handleRole(OFMessage m) throws SwitchStateException {
+
+    }
+
+    @Override
+    public boolean connectSwitch() {
+        return false;
+    }
+
+    @Override
+    public boolean activateMasterSwitch() {
+        return false;
+    }
+
+    @Override
+    public boolean activateEqualSwitch() {
+        return false;
+    }
+
+    @Override
+    public void transitionToEqualSwitch() {
+
+    }
+
+    @Override
+    public void transitionToMasterSwitch() {
+
+    }
+
+    @Override
+    public void removeConnectedSwitch() {
+
+    }
+
+    @Override
+    public void setPortDescReply(OFPortDescStatsReply portDescReply) {
+
+    }
+
+    @Override
+    public void setPortDescReplies(List<OFPortDescStatsReply> portDescReplies) {
+
+    }
+
+    @Override
+    public void setFeaturesReply(OFFeaturesReply featuresReply) {
+
+    }
+
+    @Override
+    public void setSwitchDescription(OFDescStatsReply desc) {
+
+    }
+
+    @Override
+    public int getNextTransactionId() {
+        return 0;
+    }
+
+    @Override
+    public void setOFVersion(OFVersion ofV) {
+
+    }
+
+    @Override
+    public void setTableFull(boolean full) {
+
+    }
+
+    @Override
+    public void setChannel(Channel channel) {
+
+    }
+
+    @Override
+    public void setConnected(boolean connected) {
+
+    }
+
+    @Override
+    public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
+
+    }
+
+    @Override
+    public Boolean supportNxRole() {
+        return true;
+    }
+
+    @Override
+    public void startDriverHandshake() {
+
+    }
+
+    @Override
+    public boolean isDriverHandshakeComplete() {
+        return false;
+    }
+
+    @Override
+    public void processDriverHandshakeMessage(OFMessage m) {
+
+    }
+
+    @Override
+    public void sendRoleRequest(OFMessage message) {
+
+    }
+
+    @Override
+    public void sendHandshakeMessage(OFMessage message) {
+
+    }
+
+    @Override
+    public DriverHandler handler() {
+        return null;
+    }
+
+    @Override
+    public void setHandler(DriverHandler handler) {
+
+    }
+
+    @Override
+    public DriverData data() {
+        return null;
+    }
+
+    @Override
+    public void setData(DriverData data) {
+
+    }
+
+    @Override
+    public void sendMsg(OFMessage msg) {
+
+    }
+
+    @Override
+    public void sendMsg(List<OFMessage> msgs) {
+
+    }
+
+    @Override
+    public void handleMessage(OFMessage fromSwitch) {
+
+    }
+
+    @Override
+    public void setRole(RoleState role) {
+        this.role = role;
+    }
+
+    @Override
+    public RoleState getRole() {
+        return role;
+    }
+
+    @Override
+    public List<OFPortDesc> getPorts() {
+        return null;
+    }
+
+    @Override
+    public OFFactory factory() {
+        // return what-ever triggers requestPending = true
+        return OFFactories.getFactory(OFVersion.OF_10);
+    }
+
+    @Override
+    public String getStringId() {
+        return "100";
+    }
+
+    @Override
+    public long getId() {
+        return 0;
+    }
+
+    @Override
+    public String manufacturerDescription() {
+        return null;
+    }
+
+    @Override
+    public String datapathDescription() {
+        return null;
+    }
+
+    @Override
+    public String hardwareDescription() {
+        return null;
+    }
+
+    @Override
+    public String softwareDescription() {
+        return null;
+    }
+
+    @Override
+    public String serialNumber() {
+        return null;
+    }
+
+    @Override
+    public boolean isConnected() {
+        return false;
+    }
+
+    @Override
+    public void disconnectSwitch() {
+
+    }
+
+    @Override
+    public void returnRoleReply(RoleState requested, RoleState response) {
+
+    }
+
+    @Override
+    public Device.Type deviceType() {
+        return Device.Type.SWITCH;
+    }
+
+    @Override
+    public String channelId() {
+        return null;
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/ControllerTest.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/ControllerTest.java
new file mode 100644
index 0000000..dddea32
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/ControllerTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.stream.IntStream;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestTools;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.openflow.DriverAdapter;
+import org.onosproject.openflow.DriverServiceAdapter;
+import org.onosproject.openflow.OFDescStatsReplyAdapter;
+import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.io.Files;
+
+import static com.google.common.io.ByteStreams.toByteArray;
+import static com.google.common.io.Files.write;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThan;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+
+/**
+ * Unit tests for the OpenFlow controller class.
+ */
+public class ControllerTest {
+
+    Controller controller;
+    protected static final Logger log = LoggerFactory.getLogger(ControllerTest.class);
+
+    static final File TEST_DIR = Files.createTempDir();
+
+    /*
+     * Writes the necessary file for the tests in the temporary directory
+     */
+    static File stageTestResource(String name) throws IOException {
+        File file = new File(TEST_DIR, name);
+        byte[] bytes = toByteArray(ControllerTest.class.getResourceAsStream(name));
+        write(bytes, file);
+        return file;
+    }
+
+    class MockDriverService extends DriverServiceAdapter {
+        static final int NO_SUCH_DRIVER_ID = 1;
+        static final int ITEM_NOT_FOUND_DRIVER_ID = 2;
+        static final int DRIVER_EXISTS_ID = 3;
+
+        static final String BASE_DRIVER_NAME = "of:000000000000000";
+
+        static final String NO_SUCH_DRIVER = BASE_DRIVER_NAME
+                + NO_SUCH_DRIVER_ID;
+        static final String ITEM_NOT_FOUND_DRIVER = BASE_DRIVER_NAME
+                + ITEM_NOT_FOUND_DRIVER_ID;
+        static final String DRIVER_EXISTS = BASE_DRIVER_NAME
+                + DRIVER_EXISTS_ID;
+
+        @Override
+        public Driver getDriver(DeviceId deviceId) {
+            switch (deviceId.toString()) {
+                case NO_SUCH_DRIVER:
+                    return null;
+                case ITEM_NOT_FOUND_DRIVER:
+                    throw new ItemNotFoundException();
+                case DRIVER_EXISTS:
+                    return new DriverAdapter();
+                default:
+                    throw new AssertionError();
+            }
+        }
+    }
+
+    /**
+     * Creates and initializes a new controller.
+     */
+    @Before
+    public void setUp() {
+        controller = new Controller();
+        Dictionary<String, String> properties = new Hashtable<>();
+        properties.put("openflowPorts",
+                       Integer.toString(TestTools.findAvailablePort(0)));
+        controller.setConfigParams(properties);
+    }
+
+    /**
+     * Tests fetching a driver that does not exist.
+     */
+    @Test
+    public void switchInstanceNotFoundTest() {
+        controller.start(null, new MockDriverService());
+        OpenFlowSwitchDriver driver =
+                controller.getOFSwitchInstance(MockDriverService.NO_SUCH_DRIVER_ID,
+                                               null,
+                                               null);
+        assertThat(driver, nullValue());
+        controller.stop();
+    }
+
+    /**
+     * Tests fetching a driver that throws an ItemNotFoundException.
+     */
+    @Test
+    public void switchItemNotFoundTest() {
+        controller.start(null, new MockDriverService());
+        OFDescStatsReply stats =
+                new OFDescStatsReplyAdapter();
+        OpenFlowSwitchDriver driver =
+                controller.getOFSwitchInstance(MockDriverService.ITEM_NOT_FOUND_DRIVER_ID,
+                                               stats,
+                                               null);
+        assertThat(driver, nullValue());
+        controller.stop();
+    }
+
+    /**
+     * Tests fetching a driver that throws an ItemNotFoundException.
+     */
+    @Test
+    public void driverExistsTest() {
+        controller.start(null, new MockDriverService());
+        OFDescStatsReply stats =
+                new OFDescStatsReplyAdapter();
+        OpenFlowSwitchDriver driver =
+                controller.getOFSwitchInstance(MockDriverService.DRIVER_EXISTS_ID,
+                                               stats,
+                                               null);
+        assertThat(driver, notNullValue());
+        controller.stop();
+    }
+
+    /**
+     * Tests configuring the controller.
+     */
+    @Test
+    public void testConfiguration() {
+        Dictionary<String, String> properties = new Hashtable<>();
+        properties.put("openflowPorts", "1,2,3,4,5");
+        properties.put("workerThreads", "5");
+
+        controller.setConfigParams(properties);
+        IntStream.rangeClosed(1, 5)
+                .forEach(i -> assertThat(controller.openFlowPorts, hasItem(i)));
+        assertThat(controller.workerThreads, is(5));
+    }
+
+    /**
+     * Tests the SSL/TLS methods in the controller.
+     */
+    @Test
+    public void testSsl() throws IOException {
+        File keystore = stageTestResource("ControllerTestKeystore.jks");
+        String keystoreName = keystore.getAbsolutePath();
+
+        System.setProperty("enableOFTLS", Boolean.toString(Boolean.TRUE));
+        System.setProperty("javax.net.ssl.keyStore", keystoreName);
+        System.setProperty("javax.net.ssl.trustStore", keystoreName);
+        System.setProperty("javax.net.ssl.keyStorePassword", "password");
+        System.setProperty("javax.net.ssl.trustStorePassword", "password");
+        Dictionary<String, String> properties = new Hashtable<>();
+        properties.put("openflowPorts",
+                       Integer.toString(TestTools.findAvailablePort(0)));
+        properties.put("workerThreads", "0");
+
+        controller.setConfigParams(properties);
+        controller.start(null, new MockDriverService());
+
+        assertThat(controller.serverSslEngine, notNullValue());
+
+        controller.stop();
+        boolean removed = keystore.delete();
+        if (!removed) {
+            log.warn("Could not remove temporary file");
+        }
+    }
+
+    /**
+     * Tests controll utility health methods.
+     */
+    @Test
+    public void testHealth() {
+        Map<String, Long> memory = controller.getMemory();
+        assertThat(memory.size(), is(2));
+        assertThat(memory.get("total"), is(not(0)));
+        assertThat(memory.get("free"), is(not(0)));
+
+        long startTime = controller.getSystemStartTime();
+        assertThat(startTime, lessThan(System.currentTimeMillis()));
+
+        long upTime = controller.getSystemUptime();
+        assertThat(upTime, lessThan(30L * 1000));
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageDecoderTest.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageDecoderTest.java
new file mode 100644
index 0000000..ed1db23
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageDecoderTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.junit.Test;
+import org.onosproject.openflow.ChannelAdapter;
+import org.onosproject.openflow.ChannelHandlerContextAdapter;
+import org.projectfloodlight.openflow.protocol.OFHello;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+
+/**
+ * Tests for the OpenFlow message decoder.
+ */
+public class OFMessageDecoderTest {
+
+    static class ConnectedChannel extends ChannelAdapter {
+        @Override
+        public boolean isConnected() {
+            return true;
+        }
+    }
+
+    private ChannelBuffer getHelloMessageBuffer() {
+        // OFHello, OF version 1, xid of 0, total of 8 bytes
+        byte[] messageData = {0x1, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0};
+        ChannelBuffer channelBuffer = ChannelBuffers.dynamicBuffer();
+        channelBuffer.writeBytes(messageData);
+        return channelBuffer;
+    }
+
+    /**
+     * Tests decoding a message on a closed channel.
+     *
+     * @throws Exception when an exception is thrown from the decoder
+     */
+    @Test
+    public void testDecodeNoChannel() throws Exception {
+        OFMessageDecoder decoder = new OFMessageDecoder();
+        ChannelBuffer channelBuffer = getHelloMessageBuffer();
+        Object message =
+                decoder.decode(new ChannelHandlerContextAdapter(),
+                               new ChannelAdapter(),
+                               channelBuffer);
+        assertThat(message, nullValue());
+    }
+
+    /**
+     * Tests decoding a message.
+     *
+     * @throws Exception when an exception is thrown from the decoder
+     */
+    @Test
+    public void testDecode() throws Exception {
+        OFMessageDecoder decoder = new OFMessageDecoder();
+        ChannelBuffer channelBuffer = getHelloMessageBuffer();
+        Object message =
+                decoder.decode(new ChannelHandlerContextAdapter(),
+                               new ConnectedChannel(),
+                               channelBuffer);
+        assertThat(message, notNullValue());
+        assertThat(message, instanceOf(OFHello.class));
+    }
+
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageEncoderTest.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageEncoderTest.java
new file mode 100644
index 0000000..d09e566
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OFMessageEncoderTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.junit.Test;
+import org.onosproject.openflow.OfMessageAdapter;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFType;
+
+import com.google.common.collect.ImmutableList;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+/**
+ * Tests for the OpenFlow message encoder.
+ */
+public class OFMessageEncoderTest {
+
+    static class MockOfMessage extends OfMessageAdapter {
+        static int nextId = 1;
+        final int id;
+
+        MockOfMessage() {
+            super(OFType.ERROR);
+            id = nextId++;
+        }
+
+        @Override
+        public void writeTo(ChannelBuffer channelBuffer) {
+            String message = "message" + Integer.toString(id) + " ";
+            channelBuffer.writeBytes(message.getBytes(StandardCharsets.UTF_8));
+        }
+    }
+
+    /**
+     * Tests that encoding a non-list returns the object specified.
+     *
+     * @throws Exception on exception in the encoder
+     */
+    @Test
+    public void testNoList() throws Exception {
+        OFMessageEncoder encoder = new OFMessageEncoder();
+        MockOfMessage message = new MockOfMessage();
+        OFMessage returnedMessage =
+                (OFMessage) encoder.encode(null, null, message);
+        assertThat(message, is(returnedMessage));
+    }
+
+    /**
+     * Tests that encoding a list returns the proper encoded payload.
+     *
+     * @throws Exception on exception in the encoder
+     */
+    @Test
+    public void testList() throws Exception {
+        OFMessageEncoder encoder = new OFMessageEncoder();
+        MockOfMessage message1 = new MockOfMessage();
+        MockOfMessage message2 = new MockOfMessage();
+        MockOfMessage message3 = new MockOfMessage();
+        List<MockOfMessage> messages = ImmutableList.of(message1, message2, message3);
+        ChannelBuffer returnedChannel =
+                (ChannelBuffer) encoder.encode(null, null, messages);
+        assertThat(returnedChannel, notNullValue());
+        byte[] channelBytes = returnedChannel.array();
+        String expectedListMessage = "message1 message2 message3 ";
+        String listMessage =
+                (new String(channelBytes, StandardCharsets.UTF_8))
+                        .substring(0, expectedListMessage.length());
+        assertThat(listMessage, is(expectedListMessage));
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplPacketsTest.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplPacketsTest.java
new file mode 100644
index 0000000..13086ca
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplPacketsTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.openflow.ExecutorServiceAdapter;
+import org.onosproject.openflow.MockOfFeaturesReply;
+import org.onosproject.openflow.MockOfPacketIn;
+import org.onosproject.openflow.MockOfPortStatus;
+import org.onosproject.openflow.OfMessageAdapter;
+import org.onosproject.openflow.OpenFlowSwitchListenerAdapter;
+import org.onosproject.openflow.OpenflowSwitchDriverAdapter;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowPacketContext;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.PacketListener;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFType;
+
+import static junit.framework.TestCase.fail;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Tests for packet processing in the open flow controller impl class.
+ */
+public class OpenFlowControllerImplPacketsTest {
+    OpenFlowControllerImpl controller;
+    OpenFlowControllerImpl.OpenFlowSwitchAgent agent;
+    Dpid dpid1;
+    OpenFlowSwitch switch1;
+    OpenFlowSwitchListenerAdapter switchListener;
+    TestPacketListener packetListener;
+    TestExecutorService executorService;
+
+    /**
+     * Mock packet listener that accumulates packets.
+     */
+    class TestPacketListener implements PacketListener {
+        List<OpenFlowPacketContext> contexts = new ArrayList<>();
+
+        @Override
+        public void handlePacket(OpenFlowPacketContext pktCtx) {
+            contexts.add(pktCtx);
+        }
+
+        List<OpenFlowPacketContext> contexts() {
+            return contexts;
+        }
+    }
+
+
+    /**
+     * Mock executor service that tracks submits.
+     */
+    static class TestExecutorService extends ExecutorServiceAdapter {
+        private List<OFMessage> submittedMessages = new ArrayList<>();
+
+        List<OFMessage> submittedMessages() {
+            return submittedMessages;
+        }
+
+        @Override
+        public Future<?> submit(Runnable task) {
+            OpenFlowControllerImpl.OFMessageHandler handler =
+                    (OpenFlowControllerImpl.OFMessageHandler) task;
+            submittedMessages.add(handler.msg);
+            return null;
+        }
+    }
+
+    /**
+     * Sets up switches to use as data, mocks and launches a controller instance.
+     */
+    @Before
+    public void setUp() {
+        try {
+            switch1 = new OpenflowSwitchDriverAdapter();
+            dpid1 = Dpid.dpid(new URI("of:0000000000000111"));
+        } catch (URISyntaxException ex) {
+            //  Does not happen
+            fail();
+        }
+
+        controller = new OpenFlowControllerImpl();
+        agent = controller.agent;
+        switchListener = new OpenFlowSwitchListenerAdapter();
+        controller.addListener(switchListener);
+
+        packetListener = new TestPacketListener();
+        controller.addPacketListener(100, packetListener);
+
+        executorService = new TestExecutorService();
+        controller.executorMsgs = executorService;
+    }
+
+    /**
+     * Tests a port status operation.
+     */
+    @Test
+    public void testPortStatus() {
+        OFMessage portStatusPacket = new MockOfPortStatus();
+        controller.processPacket(dpid1, portStatusPacket);
+        assertThat(switchListener.portChangedDpids().size(), is(1));
+        assertThat(switchListener.portChangedDpids().containsKey(dpid1),
+                   is(true));
+        assertThat(switchListener.portChangedDpids().get(dpid1),
+                   equalTo(portStatusPacket));
+    }
+
+    /**
+     * Tests a features reply operation.
+     */
+    @Test
+    public void testFeaturesReply() {
+        OFMessage ofFeaturesReplyPacket = new MockOfFeaturesReply();
+        controller.processPacket(dpid1, ofFeaturesReplyPacket);
+        assertThat(switchListener.changedDpids(), hasSize(1));
+        assertThat(switchListener.changedDpids().get(0),
+                   equalTo(dpid1));
+    }
+
+    /**
+     * Tests a packet in operation.
+     */
+    @Test
+    public void testPacketIn() {
+        agent.addConnectedSwitch(dpid1, switch1);
+        OFMessage packetInPacket = new MockOfPacketIn();
+        controller.processPacket(dpid1, packetInPacket);
+        assertThat(packetListener.contexts(), hasSize(1));
+    }
+
+    /**
+     * Tests an error operation.
+     */
+    @Test
+    public void testError() {
+        agent.addConnectedSwitch(dpid1, switch1);
+        OfMessageAdapter errorPacket = new OfMessageAdapter(OFType.ERROR);
+        controller.processPacket(dpid1, errorPacket);
+        assertThat(executorService.submittedMessages(), hasSize(1));
+        assertThat(executorService.submittedMessages().get(0), is(errorPacket));
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplTest.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplTest.java
new file mode 100644
index 0000000..e079c59
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImplTest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestTools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.openflow.OpenflowSwitchDriverAdapter;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.osgi.service.component.ComponentContext;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+
+import com.google.common.collect.ImmutableSet;
+
+import static junit.framework.TestCase.fail;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+
+/**
+ * Unit tests for the open flow controller implementation test.
+ */
+public class OpenFlowControllerImplTest {
+
+    OpenFlowSwitch switch1;
+    Dpid dpid1;
+    OpenFlowSwitch switch2;
+    Dpid dpid2;
+    OpenFlowSwitch switch3;
+    Dpid dpid3;
+
+    OpenFlowControllerImpl controller;
+    OpenFlowControllerImpl.OpenFlowSwitchAgent agent;
+    TestSwitchListener switchListener;
+
+    /**
+     * Test harness for a switch listener.
+     */
+    static class TestSwitchListener implements OpenFlowSwitchListener {
+        final List<Dpid> removedDpids = new ArrayList<>();
+        final List<Dpid> addedDpids = new ArrayList<>();
+        final List<Dpid> changedDpids = new ArrayList<>();
+
+        @Override
+        public void switchAdded(Dpid dpid) {
+            addedDpids.add(dpid);
+        }
+
+        @Override
+        public void switchRemoved(Dpid dpid) {
+            removedDpids.add(dpid);
+        }
+
+        @Override
+        public void switchChanged(Dpid dpid) {
+            changedDpids.add(dpid);
+        }
+
+        @Override
+        public void portChanged(Dpid dpid, OFPortStatus status) {
+            // Stub
+        }
+
+        @Override
+        public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+            // Stub
+        }
+    }
+
+
+    /**
+     * Sets up switches to use as data, mocks and launches a controller instance.
+     */
+    @Before
+    public void setUp() {
+        try {
+            switch1 = new OpenflowSwitchDriverAdapter();
+            dpid1 = Dpid.dpid(new URI("of:0000000000000111"));
+            switch2 = new OpenflowSwitchDriverAdapter();
+            dpid2 = Dpid.dpid(new URI("of:0000000000000222"));
+            switch3 = new OpenflowSwitchDriverAdapter();
+            dpid3 = Dpid.dpid(new URI("of:0000000000000333"));
+        } catch (URISyntaxException ex) {
+            //  Does not happen
+            fail();
+        }
+
+        controller = new OpenFlowControllerImpl();
+        agent = controller.agent;
+
+        switchListener = new TestSwitchListener();
+        controller.addListener(switchListener);
+
+        ComponentConfigService mockConfigService =
+                EasyMock.createMock(ComponentConfigService.class);
+        expect(mockConfigService.getProperties(anyObject())).andReturn(ImmutableSet.of());
+        mockConfigService.registerProperties(controller.getClass());
+        expectLastCall();
+        mockConfigService.unregisterProperties(controller.getClass(), false);
+        expectLastCall();
+        expect(mockConfigService.getProperties(anyObject())).andReturn(ImmutableSet.of());
+        controller.cfgService = mockConfigService;
+        replay(mockConfigService);
+
+        ComponentContext mockContext = EasyMock.createMock(ComponentContext.class);
+        Dictionary<String, String> properties = new Hashtable<>();
+        properties.put("openflowPorts",
+                       Integer.toString(TestTools.findAvailablePort(0)));
+        expect(mockContext.getProperties()).andReturn(properties);
+        replay(mockContext);
+        controller.activate(mockContext);
+    }
+
+    @After
+    public void tearDown() {
+        controller.removeListener(switchListener);
+        controller.deactivate();
+    }
+
+    /**
+     * Converts an Iterable of some type into a stream of that type.
+     *
+     * @param items Iterable of objects
+     * @param <T> type of the items in the iterable
+     * @return stream of objects of type T
+     */
+    private <T> Stream<T> makeIntoStream(Iterable<T> items) {
+        return StreamSupport.stream(
+                Spliterators.spliteratorUnknownSize(
+                        items.iterator(), Spliterator.ORDERED), false);
+    }
+
+
+    /**
+     * Tests adding and removing connected switches.
+     */
+    @Test
+    public void testAddRemoveConnectedSwitch() {
+
+        // test adding connected switches
+        boolean addSwitch1 = agent.addConnectedSwitch(dpid1, switch1);
+        assertThat(addSwitch1, is(true));
+        boolean addSwitch2 = agent.addConnectedSwitch(dpid2, switch2);
+        assertThat(addSwitch2, is(true));
+        boolean addSwitch3 = agent.addConnectedSwitch(dpid3, switch3);
+        assertThat(addSwitch3, is(true));
+
+        // Make sure the listener add callbacks fired
+        assertThat(switchListener.addedDpids, hasSize(3));
+        assertThat(switchListener.addedDpids, hasItems(dpid1, dpid2, dpid3));
+
+        // Test adding a switch twice - it should fail
+        boolean addBadSwitch1 = agent.addConnectedSwitch(dpid1, switch1);
+        assertThat(addBadSwitch1, is(false));
+
+        assertThat(controller.connectedSwitches.size(), is(3));
+
+        // test querying the switch list
+        Stream<OpenFlowSwitch> fetchedSwitches =
+                makeIntoStream(controller.getSwitches());
+        long switchCount = fetchedSwitches.count();
+        assertThat(switchCount, is(3L));
+
+        // test querying the individual switch
+        OpenFlowSwitch queriedSwitch = controller.getSwitch(dpid1);
+        assertThat(queriedSwitch, is(switch1));
+
+        // Remove a switch
+        agent.removeConnectedSwitch(dpid3);
+        Stream<OpenFlowSwitch> fetchedSwitchesAfterRemove =
+                makeIntoStream(controller.getSwitches());
+        long switchCountAfterRemove = fetchedSwitchesAfterRemove.count();
+        assertThat(switchCountAfterRemove, is(2L));
+
+        // Make sure the listener delete callbacks fired
+        assertThat(switchListener.removedDpids, hasSize(1));
+        assertThat(switchListener.removedDpids, hasItems(dpid3));
+
+        // test querying the removed switch
+        OpenFlowSwitch queriedSwitchAfterRemove = controller.getSwitch(dpid3);
+        assertThat(queriedSwitchAfterRemove, nullValue());
+    }
+
+    /**
+     * Tests adding master switches.
+     */
+    @Test
+    public void testMasterSwitch() {
+        agent.addConnectedSwitch(dpid1, switch1);
+        agent.transitionToMasterSwitch(dpid1);
+
+        Stream<OpenFlowSwitch> fetchedMasterSwitches =
+                makeIntoStream(controller.getMasterSwitches());
+        assertThat(fetchedMasterSwitches.count(), is(1L));
+        Stream<OpenFlowSwitch> fetchedActivatedSwitches =
+                makeIntoStream(controller.getEqualSwitches());
+        assertThat(fetchedActivatedSwitches.count(), is(0L));
+        OpenFlowSwitch fetchedSwitch1 = controller.getMasterSwitch(dpid1);
+        assertThat(fetchedSwitch1, is(switch1));
+
+        agent.addConnectedSwitch(dpid2, switch2);
+        boolean addSwitch2 = agent.addActivatedMasterSwitch(dpid2, switch2);
+        assertThat(addSwitch2, is(true));
+        OpenFlowSwitch fetchedSwitch2 = controller.getMasterSwitch(dpid2);
+        assertThat(fetchedSwitch2, is(switch2));
+    }
+
+    /**
+     * Tests adding equal switches.
+     */
+    @Test
+    public void testEqualSwitch() {
+        agent.addConnectedSwitch(dpid1, switch1);
+        agent.transitionToEqualSwitch(dpid1);
+
+        Stream<OpenFlowSwitch> fetchedEqualSwitches =
+                makeIntoStream(controller.getEqualSwitches());
+        assertThat(fetchedEqualSwitches.count(), is(1L));
+        Stream<OpenFlowSwitch> fetchedActivatedSwitches =
+                makeIntoStream(controller.getMasterSwitches());
+        assertThat(fetchedActivatedSwitches.count(), is(0L));
+        OpenFlowSwitch fetchedSwitch1 = controller.getEqualSwitch(dpid1);
+        assertThat(fetchedSwitch1, is(switch1));
+
+        agent.addConnectedSwitch(dpid2, switch2);
+        boolean addSwitch2 = agent.addActivatedEqualSwitch(dpid2, switch2);
+        assertThat(addSwitch2, is(true));
+        OpenFlowSwitch fetchedSwitch2 = controller.getEqualSwitch(dpid2);
+        assertThat(fetchedSwitch2, is(switch2));
+    }
+
+    /**
+     * Tests changing switch role.
+     */
+    @Test
+    public void testRoleSetting() {
+        agent.addConnectedSwitch(dpid2, switch2);
+
+        // check that state can be changed for a connected switch
+        assertThat(switch2.getRole(), is(RoleState.MASTER));
+        controller.setRole(dpid2, RoleState.EQUAL);
+        assertThat(switch2.getRole(), is(RoleState.EQUAL));
+
+        // check that changing state on an unconnected switch does not crash
+        controller.setRole(dpid3, RoleState.SLAVE);
+    }
+}
diff --git a/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/RoleManagerTest.java b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/RoleManagerTest.java
new file mode 100644
index 0000000..4b59438
--- /dev/null
+++ b/protocols/openflow/ctl/src/test/java/org/onosproject/openflow/controller/impl/RoleManagerTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.controller.impl;
+
+import java.io.IOException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.openflow.OpenflowSwitchDriverAdapter;
+import org.onosproject.openflow.controller.RoleState;
+import org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver;
+import org.onosproject.openflow.controller.driver.RoleRecvStatus;
+import org.onosproject.openflow.controller.driver.RoleReplyInfo;
+import org.onosproject.openflow.controller.driver.SwitchStateException;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFFeaturesReply;
+import org.projectfloodlight.openflow.types.U64;
+
+import static org.junit.Assert.assertEquals;
+import static org.onosproject.openflow.controller.RoleState.MASTER;
+import static org.onosproject.openflow.controller.RoleState.SLAVE;
+import static org.onosproject.openflow.controller.driver.RoleRecvStatus.MATCHED_CURRENT_ROLE;
+import static org.onosproject.openflow.controller.driver.RoleRecvStatus.OTHER_EXPECTATION;
+
+public class RoleManagerTest {
+
+    private static final U64 GID = U64.of(10L);
+    private static final long XID = 1L;
+
+    private OpenFlowSwitchDriver sw;
+    private RoleManager manager;
+
+    @Before
+    public void setUp() {
+        sw = new TestSwitchDriver();
+        manager = new RoleManager(sw);
+    }
+
+    @After
+    public void tearDown() {
+        manager = null;
+        sw = null;
+    }
+
+    @Test
+    public void deliverRoleReply() {
+        RoleRecvStatus status;
+
+        RoleReplyInfo asserted = new RoleReplyInfo(MASTER, GID, XID);
+        RoleReplyInfo unasserted = new RoleReplyInfo(SLAVE, GID, XID);
+
+        try {
+            //call without sendRoleReq() for requestPending = false
+            //first, sw.role == null
+            status = manager.deliverRoleReply(asserted);
+            assertEquals("expectation wrong", OTHER_EXPECTATION, status);
+
+            sw.setRole(MASTER);
+            assertEquals("expectation wrong", OTHER_EXPECTATION, status);
+            sw.setRole(SLAVE);
+
+            //match to pendingRole = MASTER, requestPending = true
+            manager.sendRoleRequest(MASTER, MATCHED_CURRENT_ROLE);
+            status = manager.deliverRoleReply(asserted);
+            assertEquals("expectation wrong", MATCHED_CURRENT_ROLE, status);
+
+            //requestPending never gets reset -- this might be a bug.
+            status = manager.deliverRoleReply(unasserted);
+            assertEquals("expectation wrong", OTHER_EXPECTATION, status);
+            assertEquals("pending role mismatch", MASTER, ((TestSwitchDriver) sw).failed);
+
+        } catch (IOException | SwitchStateException e) {
+            assertEquals("unexpected error thrown",
+                    SwitchStateException.class, e.getClass());
+        }
+    }
+
+    private class TestSwitchDriver extends OpenflowSwitchDriverAdapter {
+
+        RoleState failed = null;
+        RoleState current = null;
+
+        @Override
+        public void setRole(RoleState role) {
+            current = role;
+        }
+
+        @Override
+        public RoleState getRole() {
+            return current;
+        }
+
+        @Override
+        public void setFeaturesReply(OFFeaturesReply featuresReply) {
+        }
+
+        @Override
+        public void setSwitchDescription(OFDescStatsReply desc) {
+        }
+
+        @Override
+        public int getNextTransactionId() {
+            return (int) XID;
+        }
+
+        @Override
+        public void returnRoleReply(RoleState requested, RoleState response) {
+            failed = requested;
+        }
+
+        @Override
+        public String channelId() {
+            return "1.2.3.4:1";
+        }
+    }
+}
diff --git a/protocols/openflow/drivers/src/main/java/org/onosproject/openflow/drivers/OFSwitchImplSpringOpenTTPDellOSR.java b/protocols/openflow/drivers/src/main/java/org/onosproject/openflow/drivers/OFSwitchImplSpringOpenTTPDellOSR.java
new file mode 100644
index 0000000..783a37e
--- /dev/null
+++ b/protocols/openflow/drivers/src/main/java/org/onosproject/openflow/drivers/OFSwitchImplSpringOpenTTPDellOSR.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openflow.drivers;
+
+import org.onosproject.openflow.controller.Dpid;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.types.TableId;
+
+/**
+ * OFDescriptionStatistics Vendor (Manufacturer Desc.): Dell Make (Hardware
+ * Desc.) : OpenFlow 1.3 Reference Userspace Switch Model (Datapath Desc.) :
+ * None Software : Serial : None.
+ */
+//TODO: Knock-off this class as we don't need any switch/app specific
+//drivers in the south bound layers.
+public class OFSwitchImplSpringOpenTTPDellOSR extends OFSwitchImplSpringOpenTTP {
+
+    /* Table IDs to be used for Dell Open Segment Routers*/
+    private static final int DELL_TABLE_VLAN = 17;
+    private static final int DELL_TABLE_TMAC = 18;
+    private static final int DELL_TABLE_IPV4_UNICAST = 30;
+    private static final int DELL_TABLE_MPLS = 25;
+    private static final int DELL_TABLE_ACL = 40;
+
+    public OFSwitchImplSpringOpenTTPDellOSR(Dpid dpid, OFDescStatsReply desc) {
+        super(dpid, desc);
+        vlanTableId = DELL_TABLE_VLAN;
+        tmacTableId = DELL_TABLE_TMAC;
+        ipv4UnicastTableId = DELL_TABLE_IPV4_UNICAST;
+        mplsTableId = DELL_TABLE_MPLS;
+        aclTableId = DELL_TABLE_ACL;
+    }
+
+    @Override
+    public TableType getTableType(TableId tid) {
+        switch (tid.getValue()) {
+            case DELL_TABLE_IPV4_UNICAST:
+                return TableType.IP;
+            case DELL_TABLE_MPLS:
+                return TableType.MPLS;
+            case DELL_TABLE_ACL:
+                return TableType.ACL;
+            case DELL_TABLE_VLAN:
+                return TableType.VLAN;
+            case DELL_TABLE_TMAC:
+                return TableType.ETHER;
+            default:
+                log.error("Table type for Table id {} is not supported in the driver", tid);
+                return TableType.NONE;
+        }
+    }
+}
\ No newline at end of file
diff --git a/protocols/openflow/pom.xml b/protocols/openflow/pom.xml
new file mode 100644
index 0000000..3672e46
--- /dev/null
+++ b/protocols/openflow/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2014 Open Networking Laboratory
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.onosproject</groupId>
+        <artifactId>onos</artifactId>
+        <version>1.4.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>onos-of</artifactId>
+    <packaging>pom</packaging>
+
+    <description>ONOS OpenFlow Protocol subsystem</description>
+
+    <modules>
+        <module>api</module>
+        <module>ctl</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-misc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onlab-junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <version>1.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/protocols/pom.xml b/protocols/pom.xml
index 46fdc3c..6c578e9 100644
--- a/protocols/pom.xml
+++ b/protocols/pom.xml
@@ -32,8 +32,8 @@
     <description>ONOS south-bound protocols</description>
 
     <modules>
-        <!--
         <module>openflow</module>
+        <!--
         <module>ovsdb</module>
         <module>netconf</module>
         <module>pcep</module>