diff --git a/INSTALL b/INSTALL
index 7e0097b..7d0bcf3 100644
--- a/INSTALL
+++ b/INSTALL
@@ -11,6 +11,7 @@ on a specific platform, please see one of these files:
     - INSTALL.XenServer
     - INSTALL.NetBSD
     - INSTALL.DPDK
+    - INSTALL.ODP
 
 Build Requirements
 ------------------
diff --git a/INSTALL.ODP b/INSTALL.ODP
new file mode 100644
index 0000000..6dafc34
--- /dev/null
+++ b/INSTALL.ODP
@@ -0,0 +1,101 @@
+                   Using Open vSwitch with ODP
+                   ===========================
+
+
+Open vSwitch can be used with the ODP project (http://www.opendataplane.org)
+The switch will function entirely in userspace. This file serves as a guide for
+building and installing Open vSwitch with ODP.
+
+The ODP mode is considered experimental, it has not been thoroughly tested.
+
+This version of Open vSwitch should be built manually with "configure" and
+"make".
+
+
+Building and Installing:
+------------------------
+
+ODP:
+Get the code:
+git clone http://git.linaro.org/git/lng/odp.git
+cd odp
+./bootstrap.sh
+./configure --enable-debug
+make
+
+optionally: make install
+
+OVS:
+cd $(OVS_DIR)/openvswitch
+./boot.sh
+./configure --with-odp=$ODP
+make
+
+Refer to INSTALL.userspace for general requirements of building userspace OVS.
+
+Alternatively go to https://wiki.linaro.org/LNG/Engineering/OVSDPDKOnUbuntu
+which explains how to run OVS with DPDK. Similar steps should work with ODP.
+
+Using ODP with ovs-vswitchd:
+----------------------------
+
+Start ovsdb-server as discussed in INSTALL doc:
+  Summary e.g.:
+    First time only db creation (or clearing):
+      mkdir -p /usr/local/etc/openvswitch
+      mkdir -p /usr/local/var/run/openvswitch
+      rm /usr/local/etc/openvswitch/conf.db
+      cd $OVS_DIR
+      ./ovsdb/ovsdb-tool create /usr/local/etc/openvswitch/conf.db \
+        ./vswitchd/vswitch.ovsschema
+    start ovsdb-server
+      cd $OVS_DIR
+      ./ovsdb/ovsdb-server --remote=punix:/usr/local/var/run/openvswitch/db.sock \
+          --remote=db:OpenOpen_vSwitch,manager_options \
+          --private-key=db:Open_vSwitch,SSL,private_key \
+          --certificate=dbitch,SSL,certificate \
+          --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --pidfile --detach
+    First time after db creation, initialize:
+      cd $OVS_DIR
+      ./utilities/ovs-vsctl --no-wait init
+
+Start vswitchd:
+ODP configuration arguments can be passed to vswitchd via `--odp`.
+For the moment no arguments are available, but is recommended to pass --odp.
+
+  e.g.
+  export DB_SOCK=/usr/local/var/run/openvswitch/db.sock
+  ./vswitchd/ovs-vswitch --odp -- unix:$DB_SOCK  --pidfile --detach
+
+To use ovs-vswitchd with ODP, create a bridge with datapath_type
+"netdev" in the configuration database.  For example:
+
+    ovs-vsctl add-br br0
+    ovs-vsctl set bridge br0 datapath_type=netdev
+
+Now you can add ODP ports. OVS expect ODP port name to start with odp
+followed by a colon and then the interface name.
+
+    ovs-vsctl add-port br0 odp:eth0 -- set Interface odp:eth0 type=odp
+    ovs-vsctl add-port br0 odp:eth1 -- set Interface odp:eth1 type=odp
+
+For testing you can setup flows from an ODP virtual port to another port,
+an internal one for instance. Using an internal port is preferred, because
+no other packets will be involed, only what comes from the ODP port.
+
+First run ovs-ofctl to get the port ids:
+    ovs-ofctl show br0
+
+To remove all flows:
+    ovs-ofctl del-flows br0
+
+Then add a flow to direct packets comming at the ODP port to an internal port.
+    ovs-ofctl add-flow br0 in_port=1,action=output:LOCAL
+
+Then you can use tcpdump / wireshark to sniff packets on the LOCAL port.
+You might need to bring the virtual interface up:
+    ifconfig br0 up
+
+A simple test would be to use ping. In this case you should only see the
+ICMP requests showing up at the LOCAL port. Also delete the flow and check that
+packets are not forwarded anymore.
diff --git a/lib/netdev-odp.c b/lib/netdev-odp.c
new file mode 100644
index 0000000..2bbc175
--- /dev/null
+++ b/lib/netdev-odp.c
@@ -0,0 +1,735 @@
+/*
+ * Copyright (c) 2014 Nicira, 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 <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <config.h>
+#include <errno.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "dpif-netdev.h"
+#include "list.h"
+#include "netdev-odp.h"
+#include "netdev-provider.h"
+#include "netdev-vport.h"
+#include "odp-util.h"
+#include "ofp-print.h"
+#include "ofpbuf.h"
+#include "ovs-thread.h"
+#include "ovs-rcu.h"
+#include "packet-dpif.h"
+#include "packets.h"
+#include "shash.h"
+#include "sset.h"
+#include "unaligned.h"
+#include "timeval.h"
+#include "unixctl.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(odp);
+
+#define SHM_PKT_POOL_SIZE      (512*2048)
+#define SHM_PKT_POOL_BUF_SIZE  1856
+
+#define SHM_OFPBUF_POOL_SIZE      (512*256)
+#define SHM_OFPBUF_POOL_BUF_SIZE  sizeof(struct dpif_packet)
+
+static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
+
+static odp_buffer_pool_t pool;
+static odp_buffer_pool_t ofpbuf_pool;
+static odp_buffer_pool_t struct_pool;
+
+static int odp_initialized = 0;
+
+struct netdev_odp {
+    struct netdev up;
+    odp_buffer_t odp_buf; /* odp_buffer_t that holds this struct */
+    odp_pktio_t pktio;
+    odp_buffer_pool_t pkt_pool;
+    size_t frame_offset;
+    size_t max_frame_len;
+
+    struct ovs_mutex mutex OVS_ACQ_AFTER(odp_mutex);
+
+    uint8_t hwaddr[ETH_ADDR_LEN];
+    enum netdev_flags flags;
+
+    struct netdev_stats stats;
+};
+
+struct netdev_rxq_odp {
+    struct netdev_rxq up;
+    odp_buffer_t odp_buf; /* odp_buffer_t that holds this struct */
+    odp_queue_t queue_id;
+};
+
+/* We need a pool of buffers that hold netdev and rxq structures */
+#define STRUCTS_SIZE MAX(sizeof(struct netdev_odp), \
+                         sizeof(struct netdev_rxq_odp))
+#define SHM_STRUCT_POOL_SIZE       (512 * (STRUCTS_SIZE))
+#define SHM_STRUCT_POOL_BUF_SIZE   STRUCTS_SIZE
+
+int
+odp_init(int argc, char *argv[])
+{
+    int result;
+    int thr_id;
+
+    if (strcmp(argv[1], "--odp"))
+        return 0;
+
+    argc--;
+    argv++;
+
+    result = odp_init_global();
+    if (result) {
+        ODP_ERR("Error: ODP global init failed\n");
+        return result;
+    }
+
+    thr_id = odp_thread_create(0);
+    odp_init_local(thr_id);
+
+    odp_initialized = 1;
+
+    return result;
+}
+
+static int
+odp_class_init(void)
+{
+    void *pool_base;
+    int result = 0;
+
+    /* create packet pool */
+    pool_base = odp_shm_reserve("shm_packet_pool", SHM_PKT_POOL_SIZE,
+                                ODP_CACHE_LINE_SIZE);
+
+    if (odp_unlikely(pool_base == NULL)) {
+        ODP_ERR("Error: ODP packet pool mem alloc failed\n");
+        out_of_memory();
+        return -1;
+    }
+
+    pool = odp_buffer_pool_create("packet_pool", pool_base,
+                                  SHM_PKT_POOL_SIZE,
+                                  SHM_PKT_POOL_BUF_SIZE,
+                                  ODP_CACHE_LINE_SIZE,
+                                  ODP_BUFFER_TYPE_PACKET);
+
+    if (pool == ODP_BUFFER_POOL_INVALID) {
+            ODP_ERR("Error: packet pool create failed.\n");
+            return -1;
+    }
+    odp_buffer_pool_print(pool);
+
+    /* create ofpbuf pool */
+    pool_base = odp_shm_reserve("shm_ofpbuf_pool", SHM_OFPBUF_POOL_SIZE,
+                                ODP_CACHE_LINE_SIZE);
+
+    if (odp_unlikely(pool_base == NULL)) {
+        ODP_ERR("Error: ODP packet pool mem alloc failed\n");
+        out_of_memory();
+        return -1;
+    }
+
+    ofpbuf_pool = odp_buffer_pool_create("ofpbuf_packet_pool", pool_base,
+                                         SHM_OFPBUF_POOL_SIZE,
+                                         SHM_OFPBUF_POOL_BUF_SIZE,
+                                         ODP_CACHE_LINE_SIZE,
+                                         ODP_BUFFER_TYPE_RAW);
+
+    if (ofpbuf_pool == ODP_BUFFER_POOL_INVALID) {
+            ODP_ERR("Error: ofpbuf pool create failed.\n");
+            return -1;
+    }
+    odp_buffer_pool_print(ofpbuf_pool);
+
+    /* create pool for structures */
+    pool_base = odp_shm_reserve("shm_struct_pool", SHM_STRUCT_POOL_SIZE,
+                                ODP_CACHE_LINE_SIZE);
+
+    if (odp_unlikely(pool_base == NULL)) {
+        ODP_ERR("Error: ODP packet pool mem alloc failed\n");
+        out_of_memory();
+        return -1;
+    }
+
+    struct_pool = odp_buffer_pool_create("packet_pool", pool_base,
+                                         SHM_STRUCT_POOL_SIZE,
+                                         SHM_STRUCT_POOL_BUF_SIZE,
+                                         ODP_CACHE_LINE_SIZE,
+                                         ODP_BUFFER_TYPE_RAW);
+
+    if (struct_pool == ODP_BUFFER_POOL_INVALID) {
+            ODP_ERR("Error: packet pool create failed.\n");
+            return -1;
+    }
+    odp_buffer_pool_print(struct_pool);
+
+    return result;
+}
+
+static struct netdev *
+netdev_odp_alloc(void)
+{
+    struct netdev_odp *netdev;
+    odp_buffer_t buf;
+    buf = odp_buffer_alloc(struct_pool);
+    netdev = odp_buffer_addr(buf);
+    memset(netdev, 0, sizeof(*netdev));
+    netdev->odp_buf = buf;
+    return &netdev->up;
+}
+
+static struct netdev_odp *
+netdev_odp_cast(const struct netdev *netdev)
+{
+   return CONTAINER_OF(netdev, struct netdev_odp, up);
+}
+
+static int
+netdev_odp_construct(struct netdev *netdev_)
+{
+    int err = 0;
+    char *odp_if;
+    odp_pktio_params_t params;
+    socket_params_t *sock_params = &params.sock_params;
+    struct netdev_odp *netdev = netdev_odp_cast(netdev_);
+    odp_packet_t pkt;
+
+    odp_if = netdev_->name + 4; /* Names always start with "odp:" */
+
+    if (strncmp(netdev_->name, "odp:", 4)) {
+        err = ENODEV;
+        goto out_err;
+    }
+
+    sock_params->type = ODP_PKTIO_TYPE_SOCKET_BASIC;
+
+    netdev->pktio = odp_pktio_open(odp_if, pool, &params);
+
+    if (netdev->pktio == ODP_PKTIO_INVALID) {
+        ODP_ERR("Error: odp pktio failed\n");
+        err = ENODEV;
+        goto out_err;
+    }
+
+    netdev->pkt_pool = pool;
+    pkt = odp_packet_alloc(netdev->pkt_pool);
+    if (!odp_packet_is_valid(pkt)) {
+        out_of_memory();
+        goto out_err;
+    }
+
+    netdev->max_frame_len = odp_packet_buf_size(pkt);
+
+    odp_packet_free(pkt);
+
+    ovs_mutex_init(&netdev->mutex);
+
+out_err:
+
+    return err;
+}
+
+static void
+netdev_odp_destruct(struct netdev *netdev_)
+{
+    struct netdev_odp *netdev = netdev_odp_cast(netdev_);
+
+    odp_pktio_close(netdev->pktio);
+}
+
+static void
+netdev_odp_dealloc(struct netdev *netdev_)
+{
+    struct netdev_odp *netdev = netdev_odp_cast(netdev_);
+    odp_buffer_free(netdev->odp_buf);
+}
+
+static int
+netdev_odp_get_config(const struct netdev *netdev_, struct smap *args)
+{
+    struct netdev_odp *netdev = netdev_odp_cast(netdev_);
+
+    ovs_mutex_lock(&netdev->mutex);
+
+    /* TODO: Allow to configure number of queues. */
+    smap_add_format(args, "configured_rx_queues", "%u", netdev_->n_rxq);
+    smap_add_format(args, "configured_tx_queues", "%u", netdev_->n_rxq);
+    ovs_mutex_unlock(&netdev->mutex);
+
+    return 0;
+}
+
+static int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned len)
+{
+    odp_packet_t pkt;
+    unsigned pkt_cnt = len;
+    unsigned i, j;
+
+    for (i = 0, j = 0; i < len; ++i) {
+        pkt = pkt_tbl[i];
+
+        if (odp_unlikely(odp_packet_error(pkt))) {
+            odp_packet_free(pkt); /* Drop */
+            pkt_cnt--;
+        } else if (odp_unlikely(i != j++)) {
+            pkt_tbl[j-1] = pkt;
+        }
+    }
+
+    return pkt_cnt;
+}
+
+static int
+clone_pkts(struct netdev_odp *dev, struct dpif_packet **pkts,
+          odp_packet_t odp_pkts[], int cnt)
+{
+    int dropped = 0;
+    int newcnt = 0;
+    int pkts_ok = 0;
+    int i;
+
+    for (i = 0; i < cnt; i++) {
+        size_t size = ofpbuf_size(&pkts[i]->ofpbuf);
+        odp_packet_t pkt;
+
+        if (OVS_UNLIKELY(size > dev->max_frame_len)) {
+            VLOG_WARN_RL(&rl, "Too big size %u max_packet_len %u",
+                         (unsigned)size,
+                         (unsigned)dev->max_frame_len);
+            dropped++;
+            continue;
+        }
+        pkt = odp_packet_alloc(dev->pkt_pool);
+
+        if (OVS_UNLIKELY(!odp_packet_is_valid(pkt))) {
+            VLOG_WARN_RL(&rl, "Could not allocate packet");
+            dropped += cnt -i;
+            break;
+        }
+
+        odp_packet_init(pkt);
+        odp_packet_set_l2_offset(pkt, 0);
+
+        memcpy(odp_packet_l2(pkt), ofpbuf_data(&pkts[i]->ofpbuf), size);
+        odp_packet_parse(pkt, size, 0);
+
+        odp_pkts[newcnt] = pkt;
+        newcnt++;
+    }
+
+    /* Drop packets with errors */
+    pkts_ok = drop_err_pkts(odp_pkts, newcnt);
+
+    if (OVS_UNLIKELY(dropped)) {
+        ovs_mutex_lock(&dev->mutex);
+        dev->stats.tx_dropped += dropped;
+        ovs_mutex_unlock(&dev->mutex);
+    }
+
+    return pkts_ok;
+}
+
+static int
+netdev_odp_send(struct netdev *netdev, struct dpif_packet **pkts, int cnt,
+                bool may_steal)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+    odp_packet_t odp_pkts[NETDEV_MAX_RX_BATCH];
+    int pkts_ok, i;
+
+    /* Normally NETDEV_MAX_RX_BATCH should be the limit and VLA ar nasty */
+    ovs_assert(cnt <= NETDEV_MAX_RX_BATCH);
+
+    if (!may_steal || pkts[0]->ofpbuf.source != OFPBUF_ODP) {
+        pkts_ok = clone_pkts(dev, pkts, odp_pkts, cnt);
+
+        if (may_steal) {
+            for (i = 0; i < cnt; i++) {
+                dpif_packet_delete(pkts[i]);
+            }
+        }
+    } else {
+        for (i = 0; i < cnt; i++) {
+            odp_pkts[i] = pkts[i]->ofpbuf.odp_pkt;
+            odp_packet_free(pkts[i]->ofpbuf.odp_ofpbuf);
+        }
+        pkts_ok = cnt;
+    }
+
+    odp_pktio_send(dev->pktio, odp_pkts, pkts_ok);
+
+    ovs_mutex_lock(&dev->mutex);
+    dev->stats.tx_packets += pkts_ok;
+    for (i = 0; i < pkts_ok; i++) {
+        dev->stats.tx_bytes += odp_packet_get_len(odp_pkts[i]);
+    }
+    ovs_mutex_unlock(&dev->mutex);
+
+    return 0;
+}
+
+static int
+netdev_odp_set_etheraddr(struct netdev *netdev,
+                         const uint8_t mac[ETH_ADDR_LEN])
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    ovs_mutex_lock(&dev->mutex);
+    if (!eth_addr_equals(dev->hwaddr, mac)) {
+        memcpy(dev->hwaddr, mac, ETH_ADDR_LEN);
+        netdev_change_seq_changed(netdev);
+    }
+    ovs_mutex_unlock(&dev->mutex);
+
+    return 0;
+}
+
+static int
+netdev_odp_get_etheraddr(const struct netdev *netdev,
+                          uint8_t mac[ETH_ADDR_LEN])
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    ovs_mutex_lock(&dev->mutex);
+    memcpy(mac, dev->hwaddr, ETH_ADDR_LEN);
+    ovs_mutex_unlock(&dev->mutex);
+
+    return 0;
+}
+
+static int
+netdev_odp_get_mtu(const struct netdev *netdev, int *mtup)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+    (void) mtup;
+
+    return ENOTSUP;
+}
+
+static int
+netdev_odp_set_mtu(const struct netdev *netdev, int mtu)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+    (void) mtu;
+
+    return ENOTSUP;
+}
+
+static int
+netdev_odp_get_ifindex(const struct netdev *netdev)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+
+    return ENOTSUP;
+}
+
+static int
+netdev_odp_get_carrier(const struct netdev *netdev, bool *carrier)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+    (void) carrier;
+
+    return ENOTSUP;
+}
+
+static long long int
+netdev_odp_get_carrier_resets(const struct netdev *netdev)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+
+    return ENOTSUP;
+}
+
+static int
+netdev_odp_set_miimon(struct netdev *netdev_ OVS_UNUSED,
+                       long long int interval OVS_UNUSED)
+{
+    return 0;
+}
+
+static int
+netdev_odp_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    ovs_mutex_lock(&dev->mutex);
+    *stats = dev->stats;
+    ovs_mutex_unlock(&dev->mutex);
+
+    return 0;
+}
+
+static int
+netdev_odp_set_stats(struct netdev *netdev, const struct netdev_stats *stats)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    ovs_mutex_lock(&dev->mutex);
+    dev->stats = *stats;
+    ovs_mutex_unlock(&dev->mutex);
+
+    return 0;
+}
+
+static int
+netdev_odp_get_features(const struct netdev *netdev,
+                         enum netdev_features *current,
+                         enum netdev_features *advertised OVS_UNUSED,
+                         enum netdev_features *supported OVS_UNUSED,
+                         enum netdev_features *peer OVS_UNUSED)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+    (void) current;
+
+    return ENOTSUP;
+}
+
+static int
+netdev_odp_get_status(const struct netdev *netdev, struct smap *args)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+    (void) args;
+
+    return ENOTSUP;
+}
+
+static int
+netdev_odp_update_flags(struct netdev *netdev,
+                         enum netdev_flags off, enum netdev_flags on,
+                         enum netdev_flags *old_flagsp)
+{
+    struct netdev_odp *dev = netdev_odp_cast(netdev);
+
+    (void) dev;
+    (void) off;
+    (void) on;
+    (void) old_flagsp;
+
+    if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) {
+        return EINVAL;
+    }
+
+    *old_flagsp = dev->flags;
+    dev->flags |= on;
+    dev->flags &= ~off;
+
+    if (dev->flags == *old_flagsp) {
+        return 0;
+    }
+
+    return 0;
+}
+
+static struct netdev_rxq *
+netdev_odp_rxq_alloc(void)
+{
+    struct netdev_rxq_odp *rx;
+    odp_buffer_t buf;
+    buf = odp_buffer_alloc(struct_pool);
+    rx = odp_buffer_addr(buf);
+    memset(rx, 0, sizeof(*rx));
+    rx->odp_buf = buf;
+    return &rx->up;
+}
+
+static struct netdev_rxq_odp *
+netdev_rxq_odp_cast(const struct netdev_rxq *rx)
+{
+    return CONTAINER_OF(rx, struct netdev_rxq_odp, up);
+}
+
+static int
+netdev_odp_rxq_construct(struct netdev_rxq *rxq_)
+{
+    struct netdev_rxq_odp *rx = netdev_rxq_odp_cast(rxq_);
+    struct netdev_odp *netdev = netdev_odp_cast(rx->up.netdev);
+
+    rx->queue_id = odp_pktio_inq_getdef(netdev->pktio);
+
+    return 0;
+}
+
+static void
+netdev_odp_rxq_destruct(struct netdev_rxq *rxq_ OVS_UNUSED)
+{
+}
+
+static void
+netdev_odp_rxq_dealloc(struct netdev_rxq *rxq_)
+{
+    struct netdev_rxq_odp *rxq = netdev_rxq_odp_cast(rxq_);
+    odp_buffer_free(rxq->odp_buf);
+}
+
+static int
+netdev_odp_rxq_recv(struct netdev_rxq *rxq_, struct dpif_packet **packets,
+                    int *c)
+{
+    struct netdev_rxq_odp *rx = netdev_rxq_odp_cast(rxq_);
+    struct netdev_odp *netdev = netdev_odp_cast(rx->up.netdev);
+    int pkts, pkts_ok, ret = 0;
+    size_t rx_bytes = 0;
+	unsigned long err_cnt = 0;
+	int i;
+	odp_pktio_t pkt_tbl[NETDEV_MAX_RX_BATCH];
+
+    pkts = odp_pktio_recv(netdev->pktio, pkt_tbl, NETDEV_MAX_RX_BATCH);
+    if (pkts < 0) {
+        return EINVAL;
+    }
+    if (!pkts) {
+        return EAGAIN;
+    }
+
+    if (pkts > 0) {
+        pkts_ok = drop_err_pkts(pkt_tbl, pkts);
+        if (odp_unlikely(pkts_ok != pkts))
+            ODP_ERR("Dropped frames:%u - err_cnt:%lu\n",
+                    pkts-pkts_ok, ++err_cnt);
+        if (!pkts_ok) {
+            ret = EAGAIN;
+            goto out_stats;
+        }
+    }
+
+    /* Allocate an ofpbuf for each valid packet */
+    for (i = 0; i < pkts_ok; i++) {
+        odp_buffer_t buf;
+        buf = odp_buffer_alloc(ofpbuf_pool);
+        if (buf == ODP_BUFFER_INVALID) {
+            out_of_memory();
+        }
+        packets[i] = (struct dpif_packet*) odp_buffer_addr(buf);
+        ofpbuf_init_odp(&packets[i]->ofpbuf, odp_packet_buf_size(pkt_tbl[i]));
+        packets[i]->ofpbuf.odp_pkt = pkt_tbl[i];
+        packets[i]->ofpbuf.odp_ofpbuf = buf;
+        rx_bytes += odp_packet_get_len(pkt_tbl[i]);
+    }
+
+    *c = pkts_ok;
+
+    printf("ODP: received %d packets\n", pkts_ok);
+
+out_stats:
+    ovs_mutex_lock(&netdev->mutex);
+    netdev->stats.rx_packets += pkts_ok;
+    netdev->stats.rx_bytes += rx_bytes;
+    netdev->stats.rx_dropped += pkts - pkts_ok;
+    ovs_mutex_unlock(&netdev->mutex);
+
+    return ret;
+}
+
+static struct netdev_class netdev_odp_class = {
+    "odp",
+    odp_class_init,             /* init */
+    NULL,                       /* netdev_odp_run */
+    NULL,                       /* netdev_odp_wait */
+
+    netdev_odp_alloc,
+    netdev_odp_construct,
+    netdev_odp_destruct,
+    netdev_odp_dealloc,
+    netdev_odp_get_config,
+    NULL,                       /* netdev_odp_set_config */
+    NULL,                       /* get_tunnel_config */
+
+    netdev_odp_send,            /* send */
+    NULL,                       /* send_wait */
+
+    netdev_odp_set_etheraddr,
+    netdev_odp_get_etheraddr,
+    netdev_odp_get_mtu,
+    netdev_odp_set_mtu,
+    netdev_odp_get_ifindex,
+    netdev_odp_get_carrier,
+    netdev_odp_get_carrier_resets,
+    netdev_odp_set_miimon,
+    netdev_odp_get_stats,
+    netdev_odp_set_stats,
+    netdev_odp_get_features,
+    NULL,                       /* set_advertisements */
+
+    NULL,                       /* set_policing */
+    NULL,                       /* get_qos_types */
+    NULL,                       /* get_qos_capabilities */
+    NULL,                       /* get_qos */
+    NULL,                       /* set_qos */
+    NULL,                       /* get_queue */
+    NULL,                       /* set_queue */
+    NULL,                       /* delete_queue */
+    NULL,                       /* get_queue_stats */
+    NULL,                       /* queue_dump_start */
+    NULL,                       /* queue_dump_next */
+    NULL,                       /* queue_dump_done */
+    NULL,                       /* dump_queue_stats */
+
+    NULL,                       /* get_in4 */
+    NULL,                       /* set_in4 */
+    NULL,                       /* get_in6 */
+    NULL,                       /* add_router */
+    NULL,                       /* get_next_hop */
+    netdev_odp_get_status,
+    NULL,                       /* arp_lookup */
+
+    netdev_odp_update_flags,
+
+    netdev_odp_rxq_alloc,
+    netdev_odp_rxq_construct,
+    netdev_odp_rxq_destruct,
+    netdev_odp_rxq_dealloc,
+    netdev_odp_rxq_recv,
+    NULL,                       /* rxq_wait */
+    NULL,                       /* rxq_drain */
+};
+
+void
+netdev_odp_register(void)
+{
+    if (!odp_initialized) {
+        VLOG_INFO("Not running in ODP mode\n");
+        return;
+    }
+
+    netdev_register_provider(&netdev_odp_class);
+}
diff --git a/lib/netdev-odp.h b/lib/netdev-odp.h
new file mode 100644
index 0000000..a0a2594
--- /dev/null
+++ b/lib/netdev-odp.h
@@ -0,0 +1,39 @@
+#ifndef NETDEV_ODP_H
+#define NETDEV_ODP_H
+
+#include <config.h>
+#include "ofpbuf.h"
+
+#ifdef ODP_NETDEV
+
+#include <odp.h>
+#include <helper/odp_eth.h>
+#include <helper/odp_ip.h>
+#include <helper/odp_packet_helper.h>
+
+/* This function is not exported, we need another way to deal with
+   creating a packet from an ofpbuf */
+extern void odp_packet_parse(odp_packet_t pkt, size_t len, size_t l2_offset);
+
+
+void netdev_odp_register(void);
+void free_odp_buf(struct ofpbuf *);
+int odp_init(int argc, char *argv[]);
+
+#else
+
+static inline void
+netdev_odp_register(void)
+{
+    /* Nothing */
+}
+
+static inline void
+free_odp_buf(struct ofpbuf *buf OVS_UNUSED)
+{
+    /* Nothing */
+}
+
+
+#endif /* ODP_NETDEV */
+#endif
diff --git a/lib/netdev.c b/lib/netdev.c
index ea16ccb..787b4b3 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -31,6 +31,7 @@
 #include "hash.h"
 #include "list.h"
 #include "netdev-dpdk.h"
+#include "netdev-odp.h"
 #include "netdev-provider.h"
 #include "netdev-vport.h"
 #include "ofpbuf.h"
@@ -99,7 +100,8 @@ bool
 netdev_is_pmd(const struct netdev *netdev)
 {
     return (!strcmp(netdev->netdev_class->type, "dpdk") ||
-            !strcmp(netdev->netdev_class->type, "dpdkr"));
+            !strcmp(netdev->netdev_class->type, "dpdkr") ||
+            !strcmp(netdev->netdev_class->type, "odp"));
 }
 
 static void
@@ -138,6 +140,10 @@ netdev_initialize(void)
 #endif
         netdev_dpdk_register();
 
+#ifdef ODP_NETDEV
+        netdev_odp_register();
+#endif
+
         ovsthread_once_done(&once);
     }
 }
diff --git a/lib/ofpbuf.c b/lib/ofpbuf.c
index 28013d5..55c59e0 100644
--- a/lib/ofpbuf.c
+++ b/lib/ofpbuf.c
@@ -20,6 +20,7 @@
 #include <string.h>
 #include "dynamic-string.h"
 #include "netdev-dpdk.h"
+#include "netdev-odp.h"
 #include "util.h"
 
 static void
@@ -119,6 +120,14 @@ ofpbuf_init_dpdk(struct ofpbuf *b, size_t allocated)
     ofpbuf_init__(b, allocated, OFPBUF_DPDK);
 }
 
+#ifdef ODP_NETDEV
+void
+ofpbuf_init_odp(struct ofpbuf *b, size_t allocated)
+{
+    ofpbuf_init__(b, allocated, OFPBUF_ODP);
+}
+#endif
+
 /* Initializes 'b' as an empty ofpbuf with an initial capacity of 'size'
  * bytes. */
 void
@@ -142,6 +151,13 @@ ofpbuf_uninit(struct ofpbuf *b)
 #else
             ovs_assert(b->source != OFPBUF_DPDK);
 #endif
+        } else if (b->source == OFPBUF_ODP) {
+#ifdef ODP_NETDEV
+            odp_packet_free(b->odp_pkt);
+            odp_buffer_free(b->odp_ofpbuf);
+#else
+            ovs_assert(b->source != OFPBUF_ODP);
+#endif
         }
     }
 }
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index 6af9c64..ed1f26c 100644
--- a/lib/ofpbuf.h
+++ b/lib/ofpbuf.h
@@ -23,6 +23,7 @@
 #include "packets.h"
 #include "util.h"
 #include "netdev-dpdk.h"
+#include "netdev-odp.h"
 
 #ifdef  __cplusplus
 extern "C" {
@@ -62,6 +63,10 @@ struct ofpbuf {
 #ifdef DPDK_NETDEV
     struct rte_mbuf mbuf;       /* DPDK mbuf */
 #else
+# ifdef ODP_NETDEV
+    odp_buffer_t odp_ofpbuf;    /* ODP buffer containig this struct ofpbuf */
+    odp_packet_t odp_pkt;       /* ODP packet containing actual payload */
+# endif
     void *base_;                 /* First byte of allocated space. */
     void *data_;                 /* First byte actually in use. */
     uint32_t size_;              /* Number of bytes in use. */
@@ -109,6 +114,9 @@ void ofpbuf_use_stub(struct ofpbuf *, void *, size_t);
 void ofpbuf_use_const(struct ofpbuf *, const void *, size_t);
 
 void ofpbuf_init_dpdk(struct ofpbuf *b, size_t allocated);
+#ifdef ODP_NETDEV
+void ofpbuf_init_odp(struct ofpbuf *b, size_t allocated);
+#endif
 
 void ofpbuf_init(struct ofpbuf *, size_t);
 void ofpbuf_uninit(struct ofpbuf *);
@@ -183,6 +191,12 @@ static inline void ofpbuf_delete(struct ofpbuf *b)
             return;
         }
 
+        if (b->source == OFPBUF_ODP) {
+            odp_packet_free(b->odp_pkt);
+            odp_buffer_free(b->odp_ofpbuf);
+            return;
+        }
+
         ofpbuf_uninit(b);
         free(b);
     }
@@ -395,31 +409,69 @@ static inline void ofpbuf_set_size(struct ofpbuf *b, uint32_t v)
 #else
 static inline void * ofpbuf_data(const struct ofpbuf *b)
 {
+#ifdef ODP_NETDEV
+    if (b->source == OFPBUF_ODP)
+        return odp_packet_l2(b->odp_pkt);
+#endif
+
     return b->data_;
 }
 
 static inline void ofpbuf_set_data(struct ofpbuf *b, void *d)
 {
+#ifdef ODP_NETDEV
+    if (b->source == OFPBUF_ODP) {
+        ODP_ERR("ODP: Invalid use of ofpbuf_set_data\n");
+        ovs_abort(0, "Invalid function call\n");
+    }
+#endif
+
     b->data_ = d;
 }
 
 static inline void * ofpbuf_base(const struct ofpbuf *b)
 {
+#ifdef ODP_NETDEV
+    if (b->source == OFPBUF_ODP) {
+        ODP_ERR("ODP: Invalid use of ofpbuf_base\n");
+        ovs_abort(0, "Invalid function call\n");
+    }
+#endif
+
     return b->base_;
 }
 
 static inline void ofpbuf_set_base(struct ofpbuf *b, void *d)
 {
+#ifdef ODP_NETDEV
+    if (b->source == OFPBUF_ODP) {
+        ODP_ERR("ODP: Invalid use of ofpbuf_set_base\n");
+        ovs_abort(0, "Invalid function call\n");
+    }
+#endif
+
     b->base_ = d;
 }
 
 static inline uint32_t ofpbuf_size(const struct ofpbuf *b)
 {
+#ifdef ODP_NETDEV
+    if (b->source == OFPBUF_ODP)
+        return odp_packet_get_len(b->odp_pkt);
+#endif
+
     return b->size_;
 }
 
 static inline void ofpbuf_set_size(struct ofpbuf *b, uint32_t v)
 {
+#ifdef ODP_NETDEV
+    if (b->source == OFPBUF_ODP) {
+        ODP_ERR("ODP: Invalid use of ofpbuf_set_size\n");
+        ovs_abort(0, "Invalid function call\n");
+    }
+#endif
+
     b->size_ = v;
 }
 #endif
diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c
index 4d7e4f0..ba4635f 100644
--- a/vswitchd/ovs-vswitchd.c
+++ b/vswitchd/ovs-vswitchd.c
@@ -75,6 +75,12 @@ main(int argc, char *argv[])
     argc -= retval;
     argv += retval;
 
+#ifdef ODP_NETDEV
+    retval = odp_init(argc, argv);
+    argc -= retval;
+    argv += retval;
+#endif
+
     proctitle_init(argc, argv);
     service_start(&argc, &argv);
     remote = parse_options(argc, argv, &unixctl_path);
@@ -149,6 +155,7 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
         OPT_DISABLE_SYSTEM,
         DAEMON_OPTION_ENUMS,
         OPT_DPDK,
+        OPT_ODP,
     };
     static const struct option long_options[] = {
         {"help",        no_argument, NULL, 'h'},
@@ -163,6 +170,7 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
         {"enable-dummy", optional_argument, NULL, OPT_ENABLE_DUMMY},
         {"disable-system", no_argument, NULL, OPT_DISABLE_SYSTEM},
         {"dpdk", required_argument, NULL, OPT_DPDK},
+        {"odp", required_argument, NULL, OPT_ODP},
         {NULL, 0, NULL, 0},
     };
     char *short_options = long_options_to_short_options(long_options);
@@ -217,6 +225,9 @@ parse_options(int argc, char *argv[], char **unixctl_pathp)
         case OPT_DPDK:
             break;
 
+        case OPT_ODP:
+            break;
+
         default:
             abort();
         }
