diff --git a/Makefile.dep b/Makefile.dep
index 2f668d4c6027c1d9f2f6fc3288df4f12b1a4aae1..989f739324d5b4c9ce142bd20b356b00fd718cd6 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -80,6 +80,10 @@ ifneq (,$(filter at86rf231,$(USEMODULE)))
 	USEMODULE += ieee802154
 endif
 
+ifneq (,$(filter l2_ping,$(USEMODULE)))
+	USEMODULE += vtimer
+endif
+
 ifneq (,$(filter vtimer,$(USEMODULE)))
 	USEMODULE += timex
 endif
diff --git a/sys/net/include/l2_ping.h b/sys/net/include/l2_ping.h
new file mode 100644
index 0000000000000000000000000000000000000000..89f080736f5b15bd6149bf3cbeee2e614fc57c44
--- /dev/null
+++ b/sys/net/include/l2_ping.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014, INRIA
+ *
+ * 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.
+ */
+
+#ifndef L2_PING_H
+#define L2_PING_H
+
+/**
+ * @defgroup    net_l2_ping Link Layer Ping
+ * @ingroup     net
+ * @brief       Link Layer Ping
+ *
+ * @{
+ * @file
+ * @brief       Link Layer Ping public interface, data structs, and defines
+ *
+ * @author Oliver Hahm <oliver.hahm@inria.fr>
+ */
+
+#include "radio/radio.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ * @brief the bitmask of the first link layer payload byte defining the type
+ */
+#define L2_PAYLOAD_TYPE     (0xF0)
+
+/**
+ * @brief link layer payload type
+ *
+ * @note  assure not to interfere with valid network layer values like the
+ *        6LoWPAN dispatch field
+ */
+#define L2_PAYLOAD_PING     (0x10)
+
+/**
+ * @brief the bitmask for the payload identifier, defining the actual l2 ping type
+ *
+ * @see l2_ping_type_t
+ */
+#define L2_PING_TYPE        (0x0F)
+
+/**
+ * @brief the default interval between two ping packets are sent in microseconds
+ */
+#define L2_PING_DEFAULT_INTERVAL       (50 * 1000u)
+
+/**
+ * @brief maximum number of bytes for payload within a l2 ping packet
+ */
+#define L2_PING_PAYLOAD_SIZE        (8)
+
+/**
+ * @brief data type for the l2 ping type
+ */
+typedef enum {
+    L2_PING  = 0x01, /**< ping request packet */
+    L2_PONG  = 0x02, /**< pong response packet */
+    L2_PROBE = 0x04  /**< probe packet */
+} l2_ping_type_t;
+
+/**
+ * @brief l2 ping statistics data structure
+ */
+typedef struct {
+    radio_address_t dst;    /**< the destination address for the last ping request sent */
+    uint16_t ping_count;    /**< the amount of ping requests sent to this destination */
+    uint16_t pong_count;    /**< the amount of pong respnses received from this node */
+    timex_t last_rtt;       /**< the round trip time for the last received pong response */
+    timex_t avg_rtt;        /**< the average round trip time to this node */
+    timex_t max_rtt;        /**< the maximum round trip time to this node */
+    timex_t min_rtt;        /**< the minimum round trip time to this node */
+} l2_ping_stats_t;
+
+/**
+ * @brief l2 probe statistics entry
+ */
+typedef struct {
+    radio_address_t src;    /**< link layer address of the probe's origin */
+    uint16_t count;         /**< number of received probes from this node */
+} l2_probe_stat_entry_t;
+
+/**
+ * @brief payload data structure for l2 ping packets
+ */
+typedef struct ping_payload {
+    uint8_t type;                           /**< the data type */
+    uint16_t seq;                           /**< sequence number */
+    /* cppcheck-suppress unusedStructMember */
+    char payload[L2_PING_PAYLOAD_SIZE];     /**< the actual payload */
+    uint8_t payload_len;                    /**< number of payload bytes */
+} l2_ping_payload_t;
+
+/**
+ * @brief the current l2 ping statistics about the current communication
+ *        partner
+ */
+extern l2_ping_stats_t l2_ping_stats;
+
+/**
+ * @brief initializes the l2 ping module
+ *
+ * @note this function gets usually called by auto_init
+ */
+void l2_ping_init(void);
+
+/**
+ * @brief send a ping request
+ *
+ * @param[in] addr          the destination address
+ * @param[in] count         the number of requests to send, 0 means infinite
+ * @param[in] interval      the interval between sending ping requests,
+ *                          if 0 the default interval (#L2_PING_DEFAULT_INTERVAL)
+ * @param[in] payload       optional payload data, may be NULL
+ * @param[in] payload_len   the length of the payload data, must be smaller than 
+ *                          #L2_PING_PAYLOAD_SIZE
+ * @param[in] probe_only    do not request a pong response
+ */
+void l2_ping(radio_address_t addr, uint16_t count, uint32_t interval,
+             const char *payload, uint8_t payload_len, uint8_t probe_only);
+
+/**
+ * @brief get l2 probe statistics to check how many probes have been received
+ *
+ * @param[out] l2_probe_stats   pointer to an array of ::l2_probe_stat_entry_t
+ * @param[out] count            pointer to the number of entries in l2_probe_stats
+ */
+void l2_probe_stats(l2_probe_stat_entry_t *l2_probe_stats[], uint16_t *count);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* L2_PING_H */
diff --git a/sys/net/include/ping.h b/sys/net/include/ping.h
deleted file mode 100644
index 2ca378de33bbc99785c3bea13954fad57d3598fa..0000000000000000000000000000000000000000
--- a/sys/net/include/ping.h
+++ /dev/null
@@ -1,31 +0,0 @@
-
-/**
- * @defgroup    sys_ping Ping
- * @ingroup     sys
- * @brief       Ping
- */
-
-#include "radio/radio.h"
-
-#define RCV_BUFFER_SIZE     (64)
-#define RADIO_STACK_SIZE    (KERNEL_CONF_STACKSIZE_DEFAULT)
-#define PING_PAYLOAD    (8)
-
-typedef enum {
-    L2_PING,
-    L2_PONG
-} l2_ping_type_t;
-
-void ping_init(void);
-void ping(radio_address_t addr);
-
-typedef struct pong {
-    int hopcount;
-    int ttl;
-    radio_address_t radio_address;
-} ping_r;
-
-typedef struct ping_payload {
-    uint8_t type;
-    char payload[PING_PAYLOAD];
-} ping_payload;
diff --git a/sys/net/link_layer/ping/ping.c b/sys/net/link_layer/ping/ping.c
index 37523385b2a77f979deddddb3eeb47ab82e2a670..06989764a06d52a94461ab7a3938ff5e36d1ff67 100644
--- a/sys/net/link_layer/ping/ping.c
+++ b/sys/net/link_layer/ping/ping.c
@@ -1,80 +1,144 @@
-/**
- * Ping: low level ping pong
- *
+/*
  * Copyright (C) 2013, Igor Merkulow <igor.merkulow@gmail.com>
+ * Copyright (C) 2014, Oliver Hahm <oliver.hahm@inria.fr>
  *
  * This file is subject to the terms and conditions of the GNU Lesser General
- * Public License. See the file LICENSE in the top level directory for more
- * details.
+ * Public License v2.1. See the file LICENSE in the top level directory for
+ * more details.
  */
 
 /**
  * @file
  * @author Igor Merkulow <igor.merkulow@gmail.com>
+ * @author Oliver Hahm <oliver.hahm@inria.fr>
  *
  */
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+
 #include "thread.h"
 #include "msg.h"
+#include "mutex.h"
 
 #include "transceiver.h"
 #include "radio/types.h"
 #include "vtimer.h"
 #include "timex.h"
-#include "ping.h"
+#include "l2_ping.h"
 
-static void send_l2_packet(radio_address_t dst, l2_ping_type_t type);
-static void pong(radio_address_t src);
-static void *pkt_handler(void *unused);
-static void ping_handler(packet_info_t *packet_info);
+#define ENABLE_DEBUG    (0)
+#include "debug.h"
+
+/*--------------------------------------------------------------------------------------*/
+/* interal defines */
+#define RCV_BUFFER_SIZE     (64)
+#define RADIO_STACK_SIZE    (KERNEL_CONF_STACKSIZE_DEFAULT)
+#define MAX_PROB_STATS      (64)
+
+/* internal prototypes */
+/* link layer send function for l2_pings */
+static int8_t send_l2_packet(radio_address_t dst, l2_ping_type_t type, uint16_t seq, const char *payload, uint8_t payload_len);
+/* handler for incoming packets from transceiver */
+static void *l2_pkt_handler(void *unused);
+/* handles l2 ping requests */
+static void ping_handler(radio_address_t addr, uint16_t seq);
+/* handles l2 pong responses */
 static void pong_handler(void);
-static void print_success(void);
-static void print_failed(void);
+/* handle l2 probes */
+static void probe_handler(radio_address_t src);
+/* calculates the round trip time for a ping-pong exchange */
 static void calc_rtt(void);
 
-char radio_stack_buffer[RADIO_STACK_SIZE];
-msg_t msg_q[RCV_BUFFER_SIZE];
+/* internal buffers and variables */
+static char l2_pkt_handler_stack_buffer[RADIO_STACK_SIZE];
+static msg_t msg_q[RCV_BUFFER_SIZE];
 
-ping_payload pipa;
-timex_t start;
-float rtt = 0;
+#ifndef DISABLE_PROB_STATS
+static l2_probe_stat_entry_t probe_stats[MAX_PROB_STATS];
+#endif
 
-void ping_handler(packet_info_t *packet_info)
-{
-    pong(packet_info->phy_src);
-}
+static timex_t start, end, rtt_sum;
+static uint8_t ping_sent;
+static struct mutex_t ping_sender_mutex;
+l2_ping_stats_t l2_ping_stats;
 
-void ping_init(void)
+/*--------------------------------------------------------------------------------------*/
+/* public interface functions */
+void l2_ping_init(void)
 {
-    kernel_pid_t radio_pid = thread_create(radio_stack_buffer,
-                                           RADIO_STACK_SIZE, PRIORITY_MAIN - 2,
-                                           CREATE_STACKTEST, pkt_handler, NULL,
-                                           "l2_ping_handler");
+    mutex_init(&ping_sender_mutex);
+    kernel_pid_t l2_pkt_handler_pid = thread_create(l2_pkt_handler_stack_buffer,
+                                                    RADIO_STACK_SIZE,
+                                                    PRIORITY_MAIN - 2,
+                                                    CREATE_STACKTEST,
+                                                    l2_pkt_handler, NULL,
+                                                    "l2_pkt_handler");
     uint16_t transceivers = TRANSCEIVER_DEFAULT;
 
+#ifndef MODULE_NET_IF
     transceiver_init(transceivers);
     (void) transceiver_start();
-    transceiver_register(transceivers, radio_pid);
+#endif
+    transceiver_register(transceivers, l2_pkt_handler_pid);
 }
 
-void ping(radio_address_t addr)
+void l2_ping(radio_address_t addr, uint16_t count, uint32_t interval,
+             const char *payload, uint8_t payload_len, uint8_t probe_only)
 {
-    while (1) {
+    l2_ping_type_t pt;
+
+    probe_only ? (pt = L2_PROBE) : (pt = L2_PING);
+
+    if (!interval) {
+        interval = L2_PING_DEFAULT_INTERVAL;
+    }
+
+    mutex_lock(&ping_sender_mutex);
+    l2_ping_stats.dst = addr;
+    l2_ping_stats.ping_count = 0;
+    l2_ping_stats.pong_count = 0;
+    l2_ping_stats.last_rtt = timex_set(0, 0);
+    l2_ping_stats.avg_rtt = timex_set(0, 0);
+    l2_ping_stats.max_rtt = timex_set(0, 0);
+    l2_ping_stats.min_rtt = timex_set(UINT32_MAX, UINT32_MAX);
+
+    for (unsigned i = 1; (count == 0) || (i <= count); i++) {
         vtimer_now(&start);
 
-        send_l2_packet(addr, L2_PING);
-        vtimer_usleep(500 * 1000);
+        if (send_l2_packet(addr, pt, i, payload, payload_len)) {
+            ping_sent = 1;
+            l2_ping_stats.ping_count++;
+        }
+        if ((!count) || (i <= count)) {
+            vtimer_usleep(interval);
+        }
+    }
+    mutex_unlock(&ping_sender_mutex);
+}
+
+void l2_probe_stats(l2_probe_stat_entry_t *l2_probe_stats[], uint16_t *count)
+{
+    unsigned i;
+    *l2_probe_stats = probe_stats;
+    for (i = 0; i < MAX_PROB_STATS; i++) {
+        if (!(probe_stats[i]).src) {
+            break;
+        }
     }
+    *count = i;
 }
 
-static void pkt_handler(void)
+/*--------------------------------------------------------------------------------------*/
+/* internal functions */
+static void *l2_pkt_handler(void *unused)
 {
     (void) unused;
 
     msg_t m;
     radio_packet_t *p;
+    l2_ping_payload_t *pp;
 
     msg_init_queue(msg_q, sizeof(msg_q));
 
@@ -82,15 +146,38 @@ static void pkt_handler(void)
         msg_receive(&m);
 
         if (m.type == PKT_PENDING) {
+            vtimer_now(&end);
             p = (radio_packet_t *) m.content.ptr;
+            pp = (l2_ping_payload_t *) p->data;
+
+            if ((pp->type & L2_PAYLOAD_TYPE) == L2_PAYLOAD_PING) {
+                DEBUGF("INFO: received l2_ping_packet number %d from %d with payload(%d) %.*s.\n",
+                        pp->seq, p->src, pp->payload_len, pp->payload_len, pp->payload);
+                switch (pp->type & L2_PING_TYPE) {
+                    case L2_PING:
+                        ping_handler(p->src, pp->seq);
+                        break;
+                    case L2_PONG:
+                        pong_handler();
+                        break;
+                    case L2_PROBE:
+                        probe_handler(p->src);
+                        break;
+                    default:
+                        DEBUGF("ERROR: Unknown L2 PING type\n");
+                }
+            }
+            else {
+                DEBUGF("WARN: no L2 ping packet, type is %02X\n", pp->type);
+            }
 
             p->processing--;
         }
         else if (m.type == ENOBUFFER) {
-            puts("Transceiver buffer full");
+            DEBUGF("ERROR: Transceiver buffer full\n");
         }
         else {
-            puts("Unknown packet received");
+            DEBUGF("ERROR: Unknown messagereceived\n");
         }
     }
 
@@ -99,40 +186,42 @@ static void pkt_handler(void)
 
 static void calc_rtt(void)
 {
-    timex_t end;
-    vtimer_now(&end);
-    timex_t result = timex_sub(end, start);
+    timex_t rtt = timex_sub(end, start);
+    rtt_sum = timex_add(rtt_sum, rtt);
 
-    rtt = result.seconds + (float)result.microseconds / (1000.0 * 1000.0);
-}
-
-static void print_success(void)
-{
-    printf("%s%f%s\n", "time=", rtt, "ms");
-}
-
-static void print_failed(void)
-{
-    printf("%s\n", "ping failed");
-}
-
-static void pong(radio_address_t src)
-{
-    send_l2_packet(src, L2_PONG);
+    l2_ping_stats.last_rtt = rtt;
+    l2_ping_stats.avg_rtt = timex_from_uint64(timex_uint64(rtt_sum) / l2_ping_stats.pong_count);
+    if (timex_cmp(rtt, l2_ping_stats.max_rtt) > 0) {
+        l2_ping_stats.max_rtt = rtt;
+    }
+    if (timex_cmp(rtt, l2_ping_stats.min_rtt) < 0) {
+        l2_ping_stats.min_rtt = rtt;
+    }
 }
 
-static void send_l2_packet(radio_address_t dst, l2_ping_type_t type)
+static int8_t send_l2_packet(radio_address_t dst, l2_ping_type_t type, uint16_t seq, const char *payload, uint8_t payload_len)
 {
     radio_packet_t p;
+    l2_ping_payload_t pp;
+
+    if (payload_len > L2_PING_PAYLOAD_SIZE) {
+        DEBUGF("ERROR: payload too big for l2 ping packet of type %u\n", type);
+        return -1;
+    }
 
     transceiver_command_t tcmd;
     tcmd.transceivers = TRANSCEIVER_DEFAULT;
     tcmd.data = &p;
 
-    pipa.type = type;
+    pp.type = type | L2_PAYLOAD_PING;
+    pp.seq = seq;
+
+    memset(pp.payload, 0, L2_PING_PAYLOAD_SIZE);
+    memcpy(pp.payload, payload, payload_len);
+    pp.payload_len = payload_len;
 
-    p.data = (uint8_t*) &pipa;
-    p.length = sizeof(pipa);
+    p.data = (uint8_t*) &pp;
+    p.length = sizeof(pp);
     p.dst = dst;
 
     msg_t mesg;
@@ -143,12 +232,48 @@ static void send_l2_packet(radio_address_t dst, l2_ping_type_t type)
     int8_t response = mesg.content.value;
 
     if (response <= 0) {
-        print_failed();
+        DEBUGF("ERROR: sending L2 packet of type %x failed.\n", type);
     }
+
+    return response;
+}
+
+static void ping_handler(radio_address_t addr, uint16_t seq)
+{
+    send_l2_packet(addr, L2_PONG, seq, NULL, 0);
 }
 
 static void pong_handler(void)
 {
-    calc_rtt();
-    print_success();
+    if (ping_sent) {
+        ping_sent = 0;
+        l2_ping_stats.pong_count++;
+        calc_rtt();
+    }
+    else {
+        DEBUGF("ERROR: received pong without a sent ping\n");
+    }
+}
+
+static void probe_handler(radio_address_t src)
+{
+#ifdef DISABLE_PROB_STATS
+    DEBUGF("WARN: L2 probe statistics are disabled, not handling probe packet.\n");
+#else
+    unsigned i;
+    for (i = 0; i < MAX_PROB_STATS; i++) {
+        /* found entry for this source address */
+        if (probe_stats[i].src == src) {
+            probe_stats[i].count++;
+            return;
+        }
+        /* found empty entry */
+        else if (!(probe_stats[i].src)) {
+            probe_stats[i].src = src;
+            probe_stats[i].count++;
+            return;
+        }
+    }
+    DEBUGF("ERROR: L2 probe statistics full! Probe data from %u not stored.\n", src);
+#endif
 }