diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk
index fec9bf1..9702b11 100644
--- a/ovn/controller/automake.mk
+++ b/ovn/controller/automake.mk
@@ -10,6 +10,8 @@ ovn_controller_ovn_controller_SOURCES = \
      ovn/controller/lflow.h \
      ovn/controller/ofctrl.c \
      ovn/controller/ofctrl.h \
+    ovn/controller/ofcontroller.c \
+    ovn/controller/ofcontroller.h \
      ovn/controller/patch.c \
      ovn/controller/patch.h \
      ovn/controller/ovn-controller.c \
diff --git a/ovn/controller/ofcontroller.c b/ovn/controller/ofcontroller.c
new file mode 100644
index 0000000..8a6c124
--- /dev/null
+++ b/ovn/controller/ofcontroller.c
@@ -0,0 +1,231 @@
+
+/* Copyright (c) 2015 Red Hat, Inc.
+ *
+ * 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.
+ */
+
+#include <config.h>
+#include "dp-packet.h"
+#include "lflow.h"
+#include "ofctrl.h"
+#include "ofp-msgs.h"
+#include "ofp-util.h"
+#include "ofp-actions.h"
+#include "ofp-version-opt.h"
+#include "openflow/openflow.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
+#include "ovn-controller.h"
+#include "physical.h"
+#include "rconn.h"
+#include "socket-util.h"
+#include "vswitch-idl.h"
+#include "ofcontroller.h"
+
+VLOG_DEFINE_THIS_MODULE(ofcontroller);
+
+/* Passive connection for the controller */
+struct pvconn *pvconn;
+
+/* Remote connection from the switch */
+struct rconn *rconn = NULL;
+
+void
+ofcontroller_init(char const *sock_path)
+{
+    char *proto = xasprintf("punix:%s", sock_path);
+
+    pvconn_open(proto, 0, 0, &pvconn);
+    free(proto);
+}
+
+static void
+process_packet_in(struct controller_ctx *ctx OVS_UNUSED,
+                  struct ofp_header *msg)
+{
+    struct ofputil_packet_in pin;
+
+    if (ofputil_decode_packet_in(&pin, msg) != 0) {
+        return;
+    }
+    if (pin.reason != OFPR_ACTION) {
+        return;
+    }
+
+    /* XXX : process the received packet */
+}
+
+static void
+process_packet(struct controller_ctx *ctx, struct ofpbuf *msg)
+{
+    enum ofptype type;
+    struct ofpbuf b;
+
+    b = *msg;
+    if (ofptype_pull(&type, &b)) {
+        return;
+    }
+    switch (type) {
+    case OFPTYPE_HELLO:
+        {
+            uint32_t allowed_versions;
+
+            ofputil_decode_hello(msg->data, &allowed_versions);
+            /* XXX: Negotiate */
+            break;
+        }
+    case OFPTYPE_ECHO_REQUEST:
+        {
+            struct ofpbuf *r = make_echo_reply(msg->data);
+
+            rconn_send(rconn, r, NULL);
+            break;
+        }
+    case OFPTYPE_FEATURES_REPLY:
+        /* XXX: Finish this */
+        break;
+    case OFPTYPE_PACKET_IN:
+        process_packet_in(ctx, msg->data);
+        break;
+    case OFPTYPE_FLOW_REMOVED:
+    case OFPTYPE_ERROR:
+    case OFPTYPE_ECHO_REPLY:
+    case OFPTYPE_FEATURES_REQUEST:
+    case OFPTYPE_GET_CONFIG_REQUEST:
+    case OFPTYPE_GET_CONFIG_REPLY:
+    case OFPTYPE_SET_CONFIG:
+    case OFPTYPE_PORT_STATUS:
+    case OFPTYPE_PACKET_OUT:
+    case OFPTYPE_FLOW_MOD:
+    case OFPTYPE_GROUP_MOD:
+    case OFPTYPE_PORT_MOD:
+    case OFPTYPE_TABLE_MOD:
+    case OFPTYPE_BARRIER_REQUEST:
+    case OFPTYPE_BARRIER_REPLY:
+    case OFPTYPE_QUEUE_GET_CONFIG_REQUEST:
+    case OFPTYPE_QUEUE_GET_CONFIG_REPLY:
+    case OFPTYPE_DESC_STATS_REQUEST:
+    case OFPTYPE_DESC_STATS_REPLY:
+    case OFPTYPE_FLOW_STATS_REQUEST:
+    case OFPTYPE_FLOW_STATS_REPLY:
+    case OFPTYPE_AGGREGATE_STATS_REQUEST:
+    case OFPTYPE_AGGREGATE_STATS_REPLY:
+    case OFPTYPE_TABLE_STATS_REQUEST:
+    case OFPTYPE_TABLE_STATS_REPLY:
+    case OFPTYPE_PORT_STATS_REQUEST:
+    case OFPTYPE_PORT_STATS_REPLY:
+    case OFPTYPE_QUEUE_STATS_REQUEST:
+    case OFPTYPE_QUEUE_STATS_REPLY:
+    case OFPTYPE_PORT_DESC_STATS_REQUEST:
+    case OFPTYPE_PORT_DESC_STATS_REPLY:
+    case OFPTYPE_ROLE_REQUEST:
+    case OFPTYPE_ROLE_REPLY:
+    case OFPTYPE_ROLE_STATUS:
+    case OFPTYPE_REQUESTFORWARD:
+    case OFPTYPE_SET_FLOW_FORMAT:
+    case OFPTYPE_FLOW_MOD_TABLE_ID:
+    case OFPTYPE_SET_PACKET_IN_FORMAT:
+    case OFPTYPE_FLOW_AGE:
+    case OFPTYPE_SET_CONTROLLER_ID:
+    case OFPTYPE_FLOW_MONITOR_STATS_REQUEST:
+    case OFPTYPE_FLOW_MONITOR_STATS_REPLY:
+    case OFPTYPE_FLOW_MONITOR_CANCEL:
+    case OFPTYPE_FLOW_MONITOR_PAUSED:
+    case OFPTYPE_FLOW_MONITOR_RESUMED:
+    case OFPTYPE_GET_ASYNC_REQUEST:
+    case OFPTYPE_GET_ASYNC_REPLY:
+    case OFPTYPE_SET_ASYNC_CONFIG:
+    case OFPTYPE_METER_MOD:
+    case OFPTYPE_GROUP_STATS_REQUEST:
+    case OFPTYPE_GROUP_STATS_REPLY:
+    case OFPTYPE_GROUP_DESC_STATS_REQUEST:
+    case OFPTYPE_GROUP_DESC_STATS_REPLY:
+    case OFPTYPE_GROUP_FEATURES_STATS_REQUEST:
+    case OFPTYPE_GROUP_FEATURES_STATS_REPLY:
+    case OFPTYPE_METER_STATS_REQUEST:
+    case OFPTYPE_METER_STATS_REPLY:
+    case OFPTYPE_METER_CONFIG_STATS_REQUEST:
+    case OFPTYPE_METER_CONFIG_STATS_REPLY:
+    case OFPTYPE_METER_FEATURES_STATS_REQUEST:
+    case OFPTYPE_METER_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_FEATURES_STATS_REQUEST:
+    case OFPTYPE_TABLE_FEATURES_STATS_REPLY:
+    case OFPTYPE_TABLE_DESC_REQUEST:
+    case OFPTYPE_TABLE_DESC_REPLY:
+    case OFPTYPE_BUNDLE_CONTROL:
+    case OFPTYPE_BUNDLE_ADD_MESSAGE:
+    case OFPTYPE_NXT_GENEVE_TABLE_MOD:
+    case OFPTYPE_NXT_GENEVE_TABLE_REQUEST:
+    case OFPTYPE_NXT_GENEVE_TABLE_REPLY:
+    default:
+        break;
+    }
+}
+
+static void
+send_hello_packet(struct rconn *rconn)
+{
+    struct ofpbuf *ofbuf;
+
+    ofbuf = ofputil_encode_hello(rconn_get_allowed_versions(rconn));
+    rconn_send(rconn, ofbuf, NULL);
+}
+
+enum ofputil_protocol
+ofcontroller_ofp_proto(void)
+{
+    enum ofp_version version;
+
+    version = rconn_get_version(rconn);
+    return ofputil_protocol_from_ofp_version(version);
+}
+
+void
+ofcontroller_run(struct controller_ctx *ctx,
+                 const struct ovsrec_bridge *br_int)
+{
+    struct ofpbuf *msg;
+    int retval;
+    struct vconn *new_vconn = NULL;
+
+    if (br_int) {
+        retval = pvconn_accept(pvconn, &new_vconn);
+        if (!retval && new_vconn) {
+            rconn =
+                rconn_create(60, 0, DSCP_DEFAULT, 
get_allowed_ofp_versions());
+            rconn_connect_unreliably(rconn, new_vconn, NULL);
+            send_hello_packet(rconn);
+        }
+    }
+    if (rconn) {
+        rconn_run(rconn);
+        if (!rconn_is_connected(rconn)) {
+            return;
+        }
+
+        while ((msg = rconn_recv(rconn)) != NULL) {
+            process_packet(ctx, msg);
+            ofpbuf_delete(msg);
+        }
+    }
+}
+
+void
+ofcontroller_wait(void)
+{
+    if (rconn) {
+        rconn_run_wait(rconn);
+        rconn_recv_wait(rconn);
+    }
+    pvconn_wait(pvconn);
+}
diff --git a/ovn/controller/ofcontroller.h b/ovn/controller/ofcontroller.h
new file mode 100644
index 0000000..ea2d7de
--- /dev/null
+++ b/ovn/controller/ofcontroller.h
@@ -0,0 +1,33 @@
+
+/* Copyright (c) 2015 Red Hat, Inc.
+ *
+ * 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.
+ */
+
+#ifndef OFCONTROLLER_H
+#define OFCONTROLLER_H 1
+
+#include <stdint.h>
+
+/* Interface for OVN main loop. */
+void ofcontroller_init(char const *);
+void ofcontroller_run(struct controller_ctx *,
+                      const struct ovsrec_bridge *br_int);
+void ofcontroller_wait(void);
+void ofcontroller_destroy(void);
+
+/*
+ * Get the Openflow protocol supported by the client
+ */
+enum ofputil_protocol ofcontroller_ofp_proto(void);
+#endif /* netdev.h */
diff --git a/ovn/controller/ovn-controller.c 
b/ovn/controller/ovn-controller.c
index 343de28..793648f 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -42,6 +42,7 @@
  #include "util.h"

  #include "ofctrl.h"
+#include "ofcontroller.h"
  #include "binding.h"
  #include "chassis.h"
  #include "encaps.h"
@@ -274,6 +275,7 @@ main(int argc, char *argv[])
      sbrec_init();

      ofctrl_init();
+    ofcontroller_init(get_switch_controller_path());
      lflow_init();

      /* Connect to OVS OVSDB instance.  We do not monitor all tables by
@@ -337,6 +339,7 @@ main(int argc, char *argv[])
          /* Map bridges to local nets from ovn-bridge-mappings */
          if (br_int) {
              patch_run(&ctx, br_int);
+            ofcontroller_run(&ctx, br_int);
          }

          if (chassis_id) {
@@ -370,6 +373,7 @@ main(int argc, char *argv[])

          if (br_int) {
              ofctrl_wait();
+            ofcontroller_wait();
          }
          poll_block();
          if (should_service_stop()) {
