diff --git a/examples/ndn-ping/Makefile b/examples/ndn-ping/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..3ccf83aa24f4c969584a50a25ed05ba4dc399900
--- /dev/null
+++ b/examples/ndn-ping/Makefile
@@ -0,0 +1,31 @@
+# name of your application
+APPLICATION = ndn_ping
+
+# If no BOARD is found in the environment, use this default:
+BOARD ?= native
+
+# This has to be the absolute path to the RIOT base directory:
+RIOTBASE ?= $(CURDIR)/../../
+
+BOARD_INSUFFICIENT_MEMORY := chronos msb-430 msb-430h telosb weio wsn430-v1_3b wsn430-v1_4 z1 \
+                             nucleo-f042k6 nucleo-f031k6 nucleo-l031k6 nucleo-f030r8 nucleo-l053r8 \
+                             stm32f0discovery
+
+# Include packages that pull up and auto-init the link layer.
+USEMODULE += gnrc_netdev_default
+USEMODULE += auto_init_gnrc_netif
+USEMODULE += random
+USEMODULE += shell
+USEMODULE += shell_commands
+
+USEPKG += ndn-riot
+
+# Comment this out to disable code in RIOT that does safety checking
+# which is not needed in a production environment but helps in the
+# development process:
+CFLAGS += -DDEVELHELP
+
+# Change this to 0 show compiler invocation lines by default:
+QUIET ?= 1
+
+include $(RIOTBASE)/Makefile.include
diff --git a/examples/ndn-ping/README.md b/examples/ndn-ping/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d8335a9740c06d6ebef57dd3924adb203e12d881
--- /dev/null
+++ b/examples/ndn-ping/README.md
@@ -0,0 +1,49 @@
+# ndn-ping
+
+This application demonstrates the usage of the package ndn-riot.
+This example basically enables the user to setup a ndn data server, and a ndn client that can request the data.
+Any board with a default netdev can be used to run this example.
+
+# Setting up for native
+
+Create `tap` and `tapbr` devices using RIOT's `tapsetup` script before stating the application:
+```bash
+./RIOTDIR/dist/tools/tapsetup/tapsetup
+```
+
+Then run the application on 2 different terminals :
+```bash
+# on the first terminal
+make PORT=tap0 term
+# on the second terminal
+make PORT=tap1 term
+```
+
+# Usage
+
+The user can run shell commands (type "help" to see the list).
+Only one command is relative to ndn : `ndnping`.
+
+## Start a server
+
+```
+ndnping server name_uri server_id
+```
+
+Replace `name_uri` by a ndn name (for example `/test`), and `server_id` by a number.
+`server_id` will be appended to the name of the data sent by the server.
+This can help when several servers are running using the same `name_uri`, but is not useful in our example.
+
+A server will start and answer to any interest message matching the name.
+
+## Start a client
+
+```
+ndnping client name_uri max_count
+```
+
+Replace `name_uri` by a ndn name, and `max_count` by a number.
+`max_count` is the number of interest message that will be sent.
+
+A client will start, send a first interest message and wait for a data message.
+Once data is received or timeout is reached, the client can send the next interest message, or stop when the last interest have been sent.
diff --git a/examples/ndn-ping/main.c b/examples/ndn-ping/main.c
new file mode 100644
index 0000000000000000000000000000000000000000..695b11e46577799d28fbd47ff94f7ed0f9a31f29
--- /dev/null
+++ b/examples/ndn-ping/main.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Wentao Shang
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     examples
+ * @{
+ *
+ * @file
+ * @brief       NDN ping application
+ *
+ * @author      Wentao Shang <wentaoshang@gmail.com>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+
+#include "ndn-riot/ndn.h"
+#include "shell.h"
+#include "msg.h"
+
+extern int ndn_ping(int argc, char **argv);
+
+static const shell_command_t shell_commands[] = {
+    { "ndnping", "start ndn-ping client and server", ndn_ping },
+    { NULL, NULL, NULL }
+};
+
+int main(void)
+{
+    /* start shell */
+    puts("All up, running the shell now");
+    char line_buf[SHELL_DEFAULT_BUFSIZE];
+    shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
+
+    /* should be never reached */
+    return 0;
+}
diff --git a/examples/ndn-ping/ndn_ping.c b/examples/ndn-ping/ndn_ping.c
new file mode 100644
index 0000000000000000000000000000000000000000..5106aa707959bee6714ebc34c4c6dfd9c8abce44
--- /dev/null
+++ b/examples/ndn-ping/ndn_ping.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2016 Wentao Shang
+ *
+ * This file is subject to the terms and conditions of the GNU Lesser
+ * General Public License v2.1. See the file LICENSE in the top level
+ * directory for more details.
+ */
+
+/**
+ * @ingroup     examples
+ * @{
+ *
+ * @file
+ * @brief       NDN ping client and server implemetation
+ *
+ * @author      Wentao Shang <wentaoshang@gmail.com>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "thread.h"
+#include "random.h"
+
+#include "ndn-riot/app.h"
+#include "ndn-riot/ndn.h"
+#include "ndn-riot/encoding/name.h"
+#include "ndn-riot/encoding/interest.h"
+#include "ndn-riot/encoding/data.h"
+#include "ndn-riot/msg-type.h"
+
+static ndn_app_t* handle = NULL;
+
+static const uint8_t ecc_key_pri[] = {
+    0x38, 0x67, 0x54, 0x73, 0x8B, 0x72, 0x4C, 0xD6,
+    0x3E, 0xBD, 0x52, 0xF3, 0x64, 0xD8, 0xF5, 0x7F,
+    0xB5, 0xE6, 0xF2, 0x9F, 0xC2, 0x7B, 0xD6, 0x90,
+    0x42, 0x9D, 0xC8, 0xCE, 0xF0, 0xDE, 0x75, 0xB3
+};
+
+static const uint8_t ecc_key_pub[] = {
+    0x2C, 0x3C, 0x18, 0xCB, 0x31, 0x88, 0x0B, 0xC3,
+    0x73, 0xF4, 0x4A, 0xD4, 0x3F, 0x8C, 0x80, 0x24,
+    0xD4, 0x8E, 0xBE, 0xB4, 0xAD, 0xF0, 0x69, 0xA6,
+    0xFE, 0x29, 0x12, 0xAC, 0xC1, 0xE1, 0x26, 0x7E,
+    0x2B, 0x25, 0x69, 0x02, 0xD5, 0x85, 0x51, 0x4B,
+    0x91, 0xAC, 0xB9, 0xD1, 0x19, 0xE9, 0x5E, 0x97,
+    0x20, 0xBB, 0x16, 0x2A, 0xD3, 0x2F, 0xB5, 0x11,
+    0x1B, 0xD1, 0xAF, 0x76, 0xDB, 0xAD, 0xB8, 0xCE
+};
+
+static int on_data(ndn_block_t* interest, ndn_block_t* data)
+{
+    (void)interest;
+
+    ndn_block_t name;
+    int r = ndn_data_get_name(data, &name);
+    assert(r == 0);
+    printf("client (pid=%" PRIkernel_pid "): data received, name=",
+           handle->id);
+    ndn_name_print(&name);
+    putchar('\n');
+
+    ndn_block_t content;
+    r = ndn_data_get_content(data, &content);
+    assert(r == 0);
+    assert(content.len == 6);
+
+    printf("client (pid=%" PRIkernel_pid "): content=%02X%02X%02X%02X\n",
+           handle->id, *(content.buf + 2), *(content.buf + 3),
+           *(content.buf + 4), *(content.buf + 5));
+
+    r = ndn_data_verify_signature(data, ecc_key_pub, sizeof(ecc_key_pub));
+
+    if (r != 0) {
+      printf("client (pid=%" PRIkernel_pid "): fail to verify signature\n",
+             handle->id);
+    }
+    else {
+      printf("client (pid=%" PRIkernel_pid "): signature valid\n",
+             handle->id);
+    }
+
+    return NDN_APP_CONTINUE;
+}
+
+static int on_timeout(ndn_block_t* interest)
+{
+    ndn_block_t name;
+    int r = ndn_interest_get_name(interest, &name);
+    assert(r == 0);
+
+    printf("client (pid=%" PRIkernel_pid "): interest timeout, name=",
+           handle->id);
+    ndn_name_print(&name);
+    putchar('\n');
+
+    return NDN_APP_CONTINUE;
+}
+
+static uint16_t count = 0;
+static uint16_t max_count;
+
+static int send_interest(void* context)
+{
+    const char* uri = (const char*)context;
+
+    printf("client (pid=%" PRIkernel_pid "): in sched callback, count=%d\n",
+            handle->id, ++count);
+    if (count > max_count) {
+        /* This is pure hack: ideally should wait for all pending I/O requests
+        * to finish before stopping the app.  However, this may cause the app
+        * to block forever if not implemented very carefully. */
+        printf("client (pid=%" PRIkernel_pid "): stop the app\n", handle->id);
+        return NDN_APP_STOP;
+    }
+
+    ndn_shared_block_t* sn = ndn_name_from_uri(uri, strlen(uri));
+    if (sn == NULL) {
+        printf("client (pid=%" PRIkernel_pid "): cannot create name from uri "
+               "\"%s\"\n", handle->id, uri);
+        return NDN_APP_ERROR;
+    }
+
+    uint32_t rand = random_uint32();
+    ndn_shared_block_t* sin = ndn_name_append_uint32(&sn->block, rand);
+    ndn_shared_block_release(sn);
+    if (sin == NULL) {
+        printf("client (pid=%" PRIkernel_pid "): cannot append component to "
+               "name \"%s\"\n", handle->id, uri);
+        return NDN_APP_ERROR;
+    }
+
+    uint32_t lifetime = 1000;  // 1 sec
+
+    printf("client (pid=%" PRIkernel_pid "): express interest, name=",
+           handle->id);
+    ndn_name_print(&sin->block);
+    putchar('\n');
+
+    if (ndn_app_express_interest(handle, &sin->block, NULL, lifetime,
+                                 on_data, on_timeout) != 0) {
+        printf("client (pid=%" PRIkernel_pid "): failed to express interest\n",
+               handle->id);
+        ndn_shared_block_release(sin);
+        return NDN_APP_ERROR;
+    }
+    ndn_shared_block_release(sin);
+
+    if (ndn_app_schedule(handle, send_interest, context, 2000000) != 0) {
+        printf("client (pid=%" PRIkernel_pid "): cannot schedule next interest"
+               "\n", handle->id);
+        return NDN_APP_ERROR;
+    }
+    printf("client (pid=%" PRIkernel_pid "): schedule next interest in 2 sec"
+           "\n", handle->id);
+
+    return NDN_APP_CONTINUE;
+}
+
+static void run_client(const char* uri, int max_cnt)
+{
+    printf("client (pid=%" PRIkernel_pid "): start\n", thread_getpid());
+
+    handle = ndn_app_create();
+    if (handle == NULL) {
+        printf("client (pid=%" PRIkernel_pid "): cannot create app handle\n",
+               thread_getpid());
+        return;
+    }
+
+    max_count = max_cnt;
+    count = 0;
+
+    if (ndn_app_schedule(handle, send_interest, (void*)uri, 1000000) != 0) {
+        printf("client (pid=%" PRIkernel_pid "): cannot schedule first "
+               "interest\n", handle->id);
+        ndn_app_destroy(handle);
+        return;
+    }
+    printf("client (pid=%" PRIkernel_pid "): schedule first interest in 1 sec"
+           "\n", handle->id);
+
+    printf("client (pid=%" PRIkernel_pid "): enter app run loop\n",
+           handle->id);
+
+    ndn_app_run(handle);
+
+    printf("client (pid=%" PRIkernel_pid "): returned from app run loop\n",
+           handle->id);
+
+    ndn_app_destroy(handle);
+}
+
+static uint8_t sid = 0;
+
+static int on_interest(ndn_block_t* interest)
+{
+    ndn_block_t in;
+    if (ndn_interest_get_name(interest, &in) != 0) {
+        printf("server (pid=%" PRIkernel_pid "): cannot get name from interest"
+               "\n", handle->id);
+        return NDN_APP_ERROR;
+    }
+
+    printf("server (pid=%" PRIkernel_pid "): interest received, name=",
+           handle->id);
+    ndn_name_print(&in);
+    putchar('\n');
+
+    ndn_shared_block_t* sdn = ndn_name_append_uint8(&in, sid);
+    if (sdn == NULL) {
+        printf("server (pid=%" PRIkernel_pid "): cannot append component to "
+               "name\n", handle->id);
+        return NDN_APP_ERROR;
+    }
+
+    ndn_metainfo_t meta = { NDN_CONTENT_TYPE_BLOB, -1 };
+
+    uint32_t rand = random_uint32();
+    uint8_t* buf = (uint8_t*)(&rand);
+    ndn_block_t content = { buf, sizeof(rand) };
+
+    ndn_shared_block_t* sd =
+    ndn_data_create(&sdn->block, &meta, &content,
+                    NDN_SIG_TYPE_ECDSA_SHA256, NULL,
+                    ecc_key_pri, sizeof(ecc_key_pri));
+    if (sd == NULL) {
+        printf("server (pid=%" PRIkernel_pid "): cannot create data block\n",
+               handle->id);
+        ndn_shared_block_release(sdn);
+        return NDN_APP_ERROR;
+    }
+
+    printf("server (pid=%" PRIkernel_pid "): send data to NDN thread, name=",
+           handle->id);
+    ndn_name_print(&sdn->block);
+    putchar('\n');
+    ndn_shared_block_release(sdn);
+
+    /* pass ownership of "sd" to the API */
+    if (ndn_app_put_data(handle, sd) != 0) {
+        printf("server (pid=%" PRIkernel_pid "): cannot put data\n",
+               handle->id);
+        return NDN_APP_ERROR;
+    }
+
+    printf("server (pid=%" PRIkernel_pid "): return to the app\n", handle->id);
+    return NDN_APP_CONTINUE;
+}
+
+static void run_server(const char* prefix, int id)
+{
+    printf("server (pid=%" PRIkernel_pid "): start\n", thread_getpid());
+
+    handle = ndn_app_create();
+    if (handle == NULL) {
+        printf("server (pid=%" PRIkernel_pid "): cannot create app handle\n",
+               thread_getpid());
+        return;
+    }
+    sid = (uint8_t)id;
+
+    ndn_shared_block_t* sp = ndn_name_from_uri(prefix, strlen(prefix));
+    if (sp == NULL) {
+        printf("server (pid=%" PRIkernel_pid "): cannot create name from uri "
+               "\"%s\"\n", handle->id, prefix);
+        return;
+    }
+
+    printf("server (pid=%" PRIkernel_pid "): register prefix \"%s\"\n",
+           handle->id, prefix);
+    /* pass ownership of "sp" to the API */
+    if (ndn_app_register_prefix(handle, sp, on_interest) != 0) {
+        printf("server (pid=%" PRIkernel_pid "): failed to register prefix\n",
+               handle->id);
+        ndn_app_destroy(handle);
+        return;
+    }
+
+    printf("server (pid=%" PRIkernel_pid "): enter app run loop\n",
+           handle->id);
+
+    ndn_app_run(handle);
+
+    printf("server (pid=%" PRIkernel_pid "): returned from app run loop\n",
+           handle->id);
+
+    ndn_app_destroy(handle);
+}
+
+int ndn_ping(int argc, char **argv)
+{
+    if (argc < 2) {
+        printf("usage: %s [client|server]\n", argv[0]);
+        return 1;
+    }
+
+    if (strcmp(argv[1], "client") == 0) {
+        if (argc < 4) {
+            printf("usage: %s client _name_uri_ _max_count_\n", argv[0]);
+            return 1;
+        }
+
+        int max_cnt = atoi(argv[3]);
+        if (max_cnt == 0) {
+            printf("invalid max count number: %s\n", argv[3]);
+            return 1;
+        }
+
+        run_client(argv[2], max_cnt);
+    }
+    else if (strcmp(argv[1], "server") == 0) {
+        if (argc < 4) {
+            printf("usage: %s server _prefix_ _server_id_\n", argv[0]);
+            return 1;
+        }
+
+        run_server(argv[2], atoi(argv[3]));
+    }
+    else {
+        puts("error: invalid command");
+    }
+    return 0;
+}