diff --git a/Makefile.dep b/Makefile.dep
index aafc7540b39faec5f9bb8e3bc9f3a402d8642776..eb0e4c67af39b73e6205f1936b9305d329ce8ff9 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -110,6 +110,7 @@ endif
 ifneq (,$(filter netdev_tap,$(USEMODULE)))
   USEMODULE += netif
   USEMODULE += netdev_eth
+  USEMODULE += iolist
 endif
 
 ifneq (,$(filter gnrc_tftp,$(USEMODULE)))
diff --git a/boards/native/Makefile.dep b/boards/native/Makefile.dep
index 2639777a1d2d82234ff36e87173df57079a83a13..8eec472da082e4b1542813c6f8600c7bffa1eb40 100644
--- a/boards/native/Makefile.dep
+++ b/boards/native/Makefile.dep
@@ -14,6 +14,7 @@ ifneq (,$(filter can,$(USEMODULE)))
 endif
 
 ifneq (,$(filter socket_zep,$(USEMODULE)))
+  USEMODULE += iolist
   USEMODULE += netdev_ieee802154
   USEMODULE += checksum
   USEMODULE += random
diff --git a/cpu/cc2538/radio/cc2538_rf_netdev.c b/cpu/cc2538/radio/cc2538_rf_netdev.c
index ad071120fa812f1994ddd9df160f89f4a10e65fa..71f6206c86a7b9a341e4e1f7f54fa7708dbe2dc4 100644
--- a/cpu/cc2538/radio/cc2538_rf_netdev.c
+++ b/cpu/cc2538/radio/cc2538_rf_netdev.c
@@ -33,22 +33,6 @@
 
 #define _MAX_MHR_OVERHEAD   (25)
 
-static int  _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len);
-static int  _set(netdev_t *dev, netopt_t opt, const void *value, size_t value_len);
-static int  _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
-static int  _recv(netdev_t *netdev, void *buf, size_t len, void *info);
-static void _isr(netdev_t *netdev);
-static int  _init(netdev_t *dev);
-
-const netdev_driver_t cc2538_rf_driver = {
-    .get  = _get,
-    .set  = _set,
-    .send = _send,
-    .recv = _recv,
-    .isr  = _isr,
-    .init = _init,
-};
-
 /* Reference pointer for the IRQ handler */
 static netdev_t *_dev;
 
@@ -253,7 +237,7 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *value, size_t value_
     return res;
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     (void) netdev;
 
@@ -268,8 +252,8 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
        start of the FIFO, so we can come back and update it later */
     rfcore_write_byte(0);
 
-    for (unsigned i = 0; i < count; i++) {
-        pkt_len += vector[i].iov_len;
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        pkt_len += iol->iol_len;
 
         if (pkt_len > CC2538_RF_MAX_DATA_LEN) {
             DEBUG("cc2538_rf: packet too large (%u > %u)\n",
@@ -277,7 +261,7 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
             return -EOVERFLOW;
         }
 
-        rfcore_write_fifo(vector[i].iov_base, vector[i].iov_len);
+        rfcore_write_fifo(iol->iol_base, iol->iol_len);
     }
 
 #ifdef MODULE_NETSTATS_L2
@@ -407,3 +391,12 @@ static int _init(netdev_t *netdev)
 
     return 0;
 }
+
+const netdev_driver_t cc2538_rf_driver = {
+    .get  = _get,
+    .set  = _set,
+    .send = _send,
+    .recv = _recv,
+    .isr  = _isr,
+    .init = _init,
+};
diff --git a/cpu/native/netdev_tap/netdev_tap.c b/cpu/native/netdev_tap/netdev_tap.c
index 1416a77793b141299a874192e47b26d1ff994d86..2330e64f4f06acfd467614932c773165a97b401c 100644
--- a/cpu/native/netdev_tap/netdev_tap.c
+++ b/cpu/native/netdev_tap/netdev_tap.c
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/ioctl.h>
+#include <sys/uio.h>
 #include <unistd.h>
 
 /* needs to be included before native's declarations of ntohl etc. */
@@ -51,6 +52,7 @@
 
 #include "async_read.h"
 
+#include "iolist.h"
 #include "net/eui64.h"
 #include "net/netdev.h"
 #include "net/netdev/eth.h"
@@ -64,7 +66,7 @@
 
 /* netdev interface */
 static int _init(netdev_t *netdev);
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned n);
+static int _send(netdev_t *netdev, const iolist_t *iolist);
 static int _recv(netdev_t *netdev, void *buf, size_t n, void *info);
 
 static inline void _get_mac_addr(netdev_t *netdev, uint8_t *dst)
@@ -273,17 +275,20 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
     return -1;
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned n)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     netdev_tap_t *dev = (netdev_tap_t*)netdev;
-    int res = _native_writev(dev->tap_fd, vector, n);
+
+    struct iovec iov[iolist_count(iolist)];
+
+    unsigned n;
+    size_t bytes = iolist_to_iovec(iolist, iov, &n);
+
+    int res = _native_writev(dev->tap_fd, iov, n);
 #ifdef MODULE_NETSTATS_L2
-    size_t bytes = 0;
-    for (unsigned i = 0; i < n; i++) {
-        bytes += vector->iov_len;
-        vector++;
-    }
     netdev->stats.tx_bytes += bytes;
+#else
+    (void)bytes;
 #endif
     if (netdev->event_callback) {
         netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
diff --git a/cpu/native/socket_zep/socket_zep.c b/cpu/native/socket_zep/socket_zep.c
index d3e51c69ed27079c7efbf567055e11755b1fcb63..162d55ad02914fcd500a9393b110141017c30d8a 100644
--- a/cpu/native/socket_zep/socket_zep.c
+++ b/cpu/native/socket_zep/socket_zep.c
@@ -36,23 +36,6 @@
 
 #define _UNIX_NTP_ERA_OFFSET    (2208988800U)
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned n);
-static int _recv(netdev_t *netdev, void *buf, size_t n, void *info);
-static void _isr(netdev_t *netdev);
-static int _init(netdev_t *netdev);
-static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len);
-static int _set(netdev_t *netdev, netopt_t opt, const void *value,
-                size_t value_len);
-
-static const netdev_driver_t socket_zep_driver = {
-    .send = _send,
-    .recv = _recv,
-    .init = _init,
-    .isr = _isr,
-    .get = _get,
-    .set = _set,
-};
-
 static size_t _zep_hdr_fill_v2_data(socket_zep_t *dev, zep_v2_data_hdr_t *hdr,
                                     size_t payload_len)
 {
@@ -85,24 +68,23 @@ static inline size_t _zep_hdr_fill(socket_zep_t *dev, zep_hdr_t *hdr,
                                  payload_len);
 }
 
-static size_t _prep_vector(socket_zep_t *dev, const struct iovec *vector,
+static size_t _prep_vector(socket_zep_t *dev, const iolist_t *iolist,
                            unsigned n, struct iovec *out)
 {
-    size_t bytes = 0;
+    size_t bytes;
     dev->chksum_buf = 0;
 
-    for (unsigned i = 0; i < n; i++) {
-        bytes += vector[i].iov_len;
-    }
+    bytes = iolist_size(iolist);
     bytes += sizeof(uint16_t); /* FCS field */
     out[0].iov_base = &dev->snd_hdr_buf;
     out[0].iov_len = _zep_hdr_fill(dev, out[0].iov_base, bytes);
     for (unsigned i = 0; i < n; i++) {
         /* discard const qualifier, we won't change anything. Promise! */
-        out[i + 1].iov_base = vector[i].iov_base;
-        out[i + 1].iov_len = vector[i].iov_len;
+        out[i + 1].iov_base = iolist->iol_base;
+        out[i + 1].iov_len = iolist->iol_len;
         dev->chksum_buf = ucrc16_calc_le(out[i + 1].iov_base, out[i + 1].iov_len,
                                          UCRC16_CCITT_POLY_LE, dev->chksum_buf);
+        iolist = iolist->iol_next;
     }
     dev->chksum_buf = byteorder_btols(byteorder_htons(dev->chksum_buf)).u16;
     out[n + 1].iov_base = &dev->chksum_buf;
@@ -110,16 +92,17 @@ static size_t _prep_vector(socket_zep_t *dev, const struct iovec *vector,
     return bytes;
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned n)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     socket_zep_t *dev = (socket_zep_t *)netdev;
+    unsigned n = iolist_count(iolist);
     struct iovec v[n + 2];
     size_t bytes;
     int res;
 
     assert((dev != NULL) && (dev->sock_fd != 0));
-    bytes = _prep_vector(dev, vector, n, v);
-    DEBUG("socket_zep::send(%p, %p, %u)\n", (void *)netdev, (void *)vector, n);
+    bytes = _prep_vector(dev, iolist, n, v);
+    DEBUG("socket_zep::send(%p, %p, %u)\n", (void *)netdev, (void *)iolist, n);
     /* simulate TX_STARTED interrupt */
     if (netdev->event_callback) {
         dev->last_event = NETDEV_EVENT_TX_STARTED;
@@ -350,6 +333,14 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *value,
                                   value, value_len);
 }
 
+static const netdev_driver_t socket_zep_driver = {
+    .send = _send,
+    .recv = _recv,
+    .init = _init,
+    .isr = _isr,
+    .get = _get,
+    .set = _set,
+};
 
 void socket_zep_setup(socket_zep_t *dev, const socket_zep_params_t *params)
 {
diff --git a/cpu/nrf5x_common/radio/nrfmin/nrfmin.c b/cpu/nrf5x_common/radio/nrfmin/nrfmin.c
index 1d1d127869c6202e97fb10cbc993b998311a37d6..c473594572b3f66fe685a26a1ed340f0247a0266 100644
--- a/cpu/nrf5x_common/radio/nrfmin/nrfmin.c
+++ b/cpu/nrf5x_common/radio/nrfmin/nrfmin.c
@@ -325,11 +325,11 @@ void isr_radio(void)
     cortexm_isr_end();
 }
 
-static int nrfmin_send(netdev_t *dev, const struct iovec *vector, unsigned count)
+static int nrfmin_send(netdev_t *dev, const iolist_t *iolist)
 {
     (void)dev;
 
-    assert((vector != NULL) && (count > 0) && (state != STATE_OFF));
+    assert((iolist) && (state != STATE_OFF));
 
     /* wait for any ongoing transmission to finish and go into idle state */
     while (state == STATE_TX) {}
@@ -337,17 +337,17 @@ static int nrfmin_send(netdev_t *dev, const struct iovec *vector, unsigned count
 
     /* copy packet data into the transmit buffer */
     int pos = 0;
-    for (unsigned i = 0; i < count; i++) {
-        if ((pos + vector[i].iov_len) > NRFMIN_PKT_MAX) {
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        if ((pos + iol->iol_len) > NRFMIN_PKT_MAX) {
             DEBUG("[nrfmin] send: unable to do so, packet is too large!\n");
             return -EOVERFLOW;
         }
-        memcpy(&tx_buf.raw[pos], vector[i].iov_base, vector[i].iov_len);
-        pos += vector[i].iov_len;
+        memcpy(&tx_buf.raw[pos], iol->iol_base, iol->iol_len);
+        pos += iol->iol_len;
     }
 
     /* set output buffer and destination address */
-    nrfmin_hdr_t *hdr = (nrfmin_hdr_t *)vector[0].iov_base;
+    nrfmin_hdr_t *hdr = (nrfmin_hdr_t *)iolist->iol_base;
     NRF_RADIO->PACKETPTR = (uint32_t)(&tx_buf);
     NRF_RADIO->BASE0 = (CONF_ADDR_BASE | hdr->dst_addr);
 
@@ -356,7 +356,7 @@ static int nrfmin_send(netdev_t *dev, const struct iovec *vector, unsigned count
     state = STATE_TX;
     NRF_RADIO->TASKS_TXEN = 1;
 
-    return (int)count;
+    return (int)pos;
 }
 
 static int nrfmin_recv(netdev_t *dev, void *buf, size_t len, void *info)
diff --git a/cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c b/cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c
index 7bbe1424c212d8b4331b216874dba801db79b16d..5002385802ff15b3456fd9f26e29fb907a43adc7 100644
--- a/cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c
+++ b/cpu/nrf5x_common/radio/nrfmin/nrfmin_gnrc.c
@@ -79,9 +79,6 @@ static int hdr_netif_to_nrfmin(nrfmin_hdr_t *nrfmin, gnrc_pktsnip_t *pkt)
 static int gnrc_nrfmin_send(gnrc_netif_t *dev, gnrc_pktsnip_t *pkt)
 {
     int res;
-    struct iovec *vec;
-    size_t vec_len;
-    gnrc_pktsnip_t *vec_snip;
     nrfmin_hdr_t nrfmin_hdr;
 
     assert(pkt);
@@ -95,26 +92,21 @@ static int gnrc_nrfmin_send(gnrc_netif_t *dev, gnrc_pktsnip_t *pkt)
     res = hdr_netif_to_nrfmin(&nrfmin_hdr, pkt);
     if (res < 0) {
         DEBUG("[nrfmin_gnrc] send: failed to build nrfmin header\n");
-        gnrc_pktbuf_release(pkt);
-        return res;
+        goto out;
     }
 
-    /* create iovec of data */
-    vec_snip = gnrc_pktbuf_get_iovec(pkt, &vec_len);
-    if (vec_snip == NULL) {
-        DEBUG("[nrfmin_gnrc] send: failed to create IO vector\n");
-        gnrc_pktbuf_release(pkt);
-        return -ENOBUFS;
-    }
-
-    /* link first entry of the vector to the nrfmin header */
-    vec = (struct iovec *)vec_snip->data;
-    vec[0].iov_base = &nrfmin_hdr;
-    vec[0].iov_len = NRFMIN_HDR_LEN;
+    /* link first entry after netif hdr of the pkt to the nrfmin header */
+    iolist_t iolist = {
+        .iol_next = (iolist_t *)pkt->next,
+        .iol_base = &nrfmin_hdr,
+        .iol_len = NRFMIN_HDR_LEN
+    };
 
     /* and finally send out the data and release the packet */
-    res = dev->dev->driver->send(dev->dev, vec, vec_len);
-    gnrc_pktbuf_release(vec_snip);
+    res = dev->dev->driver->send(dev->dev, &iolist);
+
+out:
+    gnrc_pktbuf_release(pkt);
 
     return res;
 }
diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep
index 5392a1c03383784a84fdecef5a08e4a940c0bf35..de911d403b4a5e28b46aee9e605fd5fa2f4e4057 100644
--- a/drivers/Makefile.dep
+++ b/drivers/Makefile.dep
@@ -112,6 +112,7 @@ ifneq (,$(filter encx24j600,$(USEMODULE)))
 endif
 
 ifneq (,$(filter ethos,$(USEMODULE)))
+  USEMODULE += iolist
   USEMODULE += netdev_eth
   USEMODULE += random
   USEMODULE += tsrb
@@ -306,6 +307,7 @@ endif
 ifneq (,$(filter sx127%,$(USEMODULE)))
   FEATURES_REQUIRED += periph_gpio
   FEATURES_REQUIRED += periph_spi
+  USEMODULE += iolist
   USEMODULE += xtimer
   USEMODULE += sx127x
   USEMODULE += netif
diff --git a/drivers/at86rf2xx/at86rf2xx_netdev.c b/drivers/at86rf2xx/at86rf2xx_netdev.c
index 54fbd9d9bba2102317e0fdb26dca17c312e2edd3..dd697483dcc2372e2eb0eda2b9b19ca220ab70b3 100644
--- a/drivers/at86rf2xx/at86rf2xx_netdev.c
+++ b/drivers/at86rf2xx/at86rf2xx_netdev.c
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2015 Freie Universität Berlin
+ * Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
+ *               2015 Freie Universität Berlin
  *
  * 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
@@ -17,6 +18,7 @@
  * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
  * @author      Kévin Roussel <Kevin.Roussel@inria.fr>
  * @author      Martine Lenders <mlenders@inf.fu-berlin.de>
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
  *
  * @}
  */
@@ -25,6 +27,8 @@
 #include <assert.h>
 #include <errno.h>
 
+#include "iolist.h"
+
 #include "net/eui64.h"
 #include "net/ieee802154.h"
 #include "net/netdev.h"
@@ -40,7 +44,7 @@
 
 #define _MAX_MHR_OVERHEAD   (25)
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
+static int _send(netdev_t *netdev, const iolist_t *iolist);
 static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
 static int _init(netdev_t *netdev);
 static void _isr(netdev_t *netdev);
@@ -93,18 +97,17 @@ static int _init(netdev_t *netdev)
     return 0;
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     at86rf2xx_t *dev = (at86rf2xx_t *)netdev;
-    const struct iovec *ptr = vector;
     size_t len = 0;
 
     at86rf2xx_tx_prepare(dev);
 
     /* load packet data into FIFO */
-    for (unsigned i = 0; i < count; ++i, ++ptr) {
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
         /* current packet data + FCS too long */
-        if ((len + ptr->iov_len + 2) > AT86RF2XX_MAX_PKT_LENGTH) {
+        if ((len + iol->iol_len + 2) > AT86RF2XX_MAX_PKT_LENGTH) {
             DEBUG("[at86rf2xx] error: packet too large (%u byte) to be send\n",
                   (unsigned)len + 2);
             return -EOVERFLOW;
@@ -112,7 +115,7 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
 #ifdef MODULE_NETSTATS_L2
         netdev->stats.tx_bytes += len;
 #endif
-        len = at86rf2xx_tx_load(dev, ptr->iov_base, ptr->iov_len, len);
+        len = at86rf2xx_tx_load(dev, iol->iol_base, iol->iol_len, len);
     }
 
     /* send data out directly if pre-loading id disabled */
diff --git a/drivers/cc110x/cc110x-netdev.c b/drivers/cc110x/cc110x-netdev.c
index 016c2bfba84f6ed4743ceb5cb185fba5682f5d43..a1b42c59da4817305f70d2d08b3c40703bf3954b 100644
--- a/drivers/cc110x/cc110x-netdev.c
+++ b/drivers/cc110x/cc110x-netdev.c
@@ -37,14 +37,12 @@
 #define ENABLE_DEBUG    (0)
 #include "debug.h"
 
-static int _send(netdev_t *dev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *dev, const iolist_t *iolist)
 {
     DEBUG("%s:%u\n", __func__, __LINE__);
 
-    (void)count;
-
-    netdev_cc110x_t *netdev_cc110x = (netdev_cc110x_t*) dev;
-    cc110x_pkt_t *cc110x_pkt = vector[0].iov_base;
+    netdev_cc110x_t *netdev_cc110x = (netdev_cc110x_t *)dev;
+    cc110x_pkt_t *cc110x_pkt = iolist->iol_base;
 
     return cc110x_send(&netdev_cc110x->cc110x, cc110x_pkt);
 }
diff --git a/drivers/cc110x/gnrc_cc110x/gnrc_cc110x.c b/drivers/cc110x/gnrc_cc110x/gnrc_cc110x.c
index abbad5703a12bd14a71e081c0fd2a9bbfb40c7cb..83db2cef2cc51dac1f6c3ca27a45f25ab717c7ed 100644
--- a/drivers/cc110x/gnrc_cc110x/gnrc_cc110x.c
+++ b/drivers/cc110x/gnrc_cc110x/gnrc_cc110x.c
@@ -71,9 +71,10 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
             cc110x_pkt.flags = 0;
     }
 
-    struct iovec vector;
-    vector.iov_base = (char*)&cc110x_pkt;
-    vector.iov_len = sizeof(cc110x_pkt_t);
+    iolist_t iolist = {
+        .iol_base = (char *)&cc110x_pkt,
+        .iol_len = sizeof(cc110x_pkt_t)
+    };
 
     unsigned payload_len = 0;
     uint8_t *pos = cc110x_pkt.data;
@@ -93,7 +94,7 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
         payload = payload->next;
     }
 
-    /* pkt has been copied into iovec, we're done with it. */
+    /* pkt has been copied into cc110x_pkt, we're done with it. */
     gnrc_pktbuf_release(pkt);
 
     cc110x_pkt.length = (uint8_t) payload_len + CC110X_HEADER_LENGTH;
@@ -104,7 +105,7 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
             (unsigned)cc110x_pkt.address,
             (unsigned)cc110x_pkt.length);
 
-    return dev->driver->send(dev, &vector, 1);
+    return dev->driver->send(dev, &iolist);
 }
 
 static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
diff --git a/drivers/cc2420/cc2420.c b/drivers/cc2420/cc2420.c
index eca7ef945e1ecc9e91fd15a4a697b1922e054d9b..a0b0600b53152835bd29fc4fa7543515b447ebe8 100644
--- a/drivers/cc2420/cc2420.c
+++ b/drivers/cc2420/cc2420.c
@@ -114,9 +114,9 @@ bool cc2420_cca(cc2420_t *dev)
     return gpio_read(dev->params.pin_cca);
 }
 
-size_t cc2420_send(cc2420_t *dev, const struct iovec *data, unsigned count)
+size_t cc2420_send(cc2420_t *dev, const iolist_t *iolist)
 {
-    size_t n = cc2420_tx_prepare(dev, data, count);
+    size_t n = cc2420_tx_prepare(dev, iolist);
 
     if ((n > 0) && !(dev->options & CC2420_OPT_PRELOADING)) {
         cc2420_tx_exec(dev);
@@ -125,7 +125,7 @@ size_t cc2420_send(cc2420_t *dev, const struct iovec *data, unsigned count)
     return n;
 }
 
-size_t cc2420_tx_prepare(cc2420_t *dev, const struct iovec *data, unsigned count)
+size_t cc2420_tx_prepare(cc2420_t *dev, const iolist_t *iolist)
 {
     size_t pkt_len = 2;     /* include the FCS (frame check sequence) */
 
@@ -134,8 +134,8 @@ size_t cc2420_tx_prepare(cc2420_t *dev, const struct iovec *data, unsigned count
     while (cc2420_get_state(dev) & NETOPT_STATE_TX) {}
 
     /* get and check the length of the packet */
-    for (unsigned i = 0; i < count; i++) {
-        pkt_len += data[i].iov_len;
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        pkt_len += iol->iol_len;
     }
     if (pkt_len >= CC2420_PKT_MAXLEN) {
         DEBUG("cc2420: tx_prep: unable to send, pkt too large\n");
@@ -147,8 +147,8 @@ size_t cc2420_tx_prepare(cc2420_t *dev, const struct iovec *data, unsigned count
     /* push packet length to TX FIFO */
     cc2420_fifo_write(dev, (uint8_t *)&pkt_len, 1);
     /* push packet to TX FIFO */
-    for (unsigned i = 0; i < count; i++) {
-        cc2420_fifo_write(dev, (uint8_t *)data[i].iov_base, data[i].iov_len);
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        cc2420_fifo_write(dev, iol->iol_base, iol->iol_len);
     }
     DEBUG("cc2420: tx_prep: loaded %i byte into the TX FIFO\n", (int)pkt_len);
 
diff --git a/drivers/cc2420/cc2420_netdev.c b/drivers/cc2420/cc2420_netdev.c
index 5514e35c88bcd48a5539cb6e74fecb9c1fa25175..fe1f42ccd300662f1445111ae7a9d577c140d985 100644
--- a/drivers/cc2420/cc2420_netdev.c
+++ b/drivers/cc2420/cc2420_netdev.c
@@ -39,7 +39,7 @@
 #include "debug.h"
 
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
+static int _send(netdev_t *netdev, const iolist_t *iolist);
 static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
 static int _init(netdev_t *netdev);
 static void _isr(netdev_t *netdev);
@@ -150,10 +150,10 @@ static void _isr(netdev_t *netdev)
     netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     cc2420_t *dev = (cc2420_t *)netdev;
-    return (int)cc2420_send(dev, vector, count);
+    return (int)cc2420_send(dev, iolist);
 }
 
 static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
diff --git a/drivers/enc28j60/enc28j60.c b/drivers/enc28j60/enc28j60.c
index 2b9dd9312dd25f1fb542085d2d5131152d718327..2f55e699a0392b68a1cfac1491ab562511c98ba9 100644
--- a/drivers/enc28j60/enc28j60.c
+++ b/drivers/enc28j60/enc28j60.c
@@ -240,7 +240,7 @@ static void on_int(void *arg)
     netdev->event_callback(arg, NETDEV_EVENT_ISR);
 }
 
-static int nd_send(netdev_t *netdev, const struct iovec *data, unsigned count)
+static int nd_send(netdev_t *netdev, const iolist_t *iolist)
 {
     enc28j60_t *dev = (enc28j60_t *)netdev;
     uint8_t ctrl = 0;
@@ -256,9 +256,9 @@ static int nd_send(netdev_t *netdev, const struct iovec *data, unsigned count)
     cmd_w_addr(dev, ADDR_WRITE_PTR, BUF_TX_START);
     /* write control byte and the actual data into the buffer */
     cmd_wbm(dev, &ctrl, 1);
-    for (unsigned i = 0; i < count; i++) {
-        c += data[i].iov_len;
-        cmd_wbm(dev, (uint8_t *)data[i].iov_base, data[i].iov_len);
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        c += iol->iol_len;
+        cmd_wbm(dev, iol->iol_base, iol->iol_len);
     }
     /* set TX end pointer */
     cmd_w_addr(dev, ADDR_TX_END, cmd_r_addr(dev, ADDR_WRITE_PTR) - 1);
diff --git a/drivers/encx24j600/encx24j600.c b/drivers/encx24j600/encx24j600.c
index 491fdcbdd0ba65f7b8bd540150fa74c0ad7703d3..47c5d3d32f41416eaee10451f297a20864f214fb 100644
--- a/drivers/encx24j600/encx24j600.c
+++ b/drivers/encx24j600/encx24j600.c
@@ -64,7 +64,7 @@ static inline int _packets_available(encx24j600_t *dev);
 static void _get_mac_addr(netdev_t *dev, uint8_t* buf);
 
 /* netdev interface */
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
+static int _send(netdev_t *netdev, const iolist_t *iolist);
 static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
 static int _init(netdev_t *dev);
 static void _isr(netdev_t *dev);
@@ -291,7 +291,7 @@ static int _init(netdev_t *encdev)
     return 0;
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count) {
+static int _send(netdev_t *netdev, const iolist_t *iolist) {
     encx24j600_t * dev = (encx24j600_t *) netdev;
     lock(dev);
 
@@ -301,9 +301,9 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count) {
     /* copy packet to SRAM */
     size_t len = 0;
 
-    for (unsigned i = 0; i < count; i++) {
-        sram_op(dev, ENC_WGPDATA, (i ? 0xFFFF : TX_BUFFER_START), vector[i].iov_base, vector[i].iov_len);
-        len += vector[i].iov_len;
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        sram_op(dev, ENC_WGPDATA, ((iol == iolist) ? TX_BUFFER_START : 0xFFFF), iol->iol_base, iol->iol_len);
+        len += iol->iol_len;
     }
 
     /* set start of TX packet and length */
diff --git a/drivers/ethos/ethos.c b/drivers/ethos/ethos.c
index 1d7c816144b534ac5e0a7b0963e0618305a39a20..511eec2865b0efe497276f4e40a1d7fc0a71b453 100644
--- a/drivers/ethos/ethos.c
+++ b/drivers/ethos/ethos.c
@@ -189,12 +189,11 @@ static int _init(netdev_t *encdev)
     return 0;
 }
 
-static size_t iovec_count_total(const struct iovec *vector, int count)
+static size_t iolist_count_total(const iolist_t *iolist)
 {
     size_t result = 0;
-    while(count--) {
-        result += vector->iov_len;
-        vector++;
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        result += iol->iol_len;
     }
     return result;
 }
@@ -256,13 +255,13 @@ void ethos_send_frame(ethos_t *dev, const uint8_t *data, size_t len, unsigned fr
     }
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     ethos_t * dev = (ethos_t *) netdev;
     (void)dev;
 
     /* count total packet length */
-    size_t pktlen = iovec_count_total(vector, count);
+    size_t pktlen = iolist_count_total(iolist);
 
     /* lock line in order to prevent multiple writes */
     mutex_lock(&dev->out_mutex);
@@ -271,14 +270,13 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
     uint8_t frame_delim = ETHOS_FRAME_DELIMITER;
     uart_write(dev->uart, &frame_delim, 1);
 
-    /* send iovec */
-    while(count--) {
-        size_t n = vector->iov_len;
-        uint8_t *ptr = vector->iov_base;
+    /* send iolist */
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        size_t n = iol->iol_len;
+        uint8_t *ptr = iol->iol_base;
         while(n--) {
             _write_escaped(dev->uart, *ptr++);
         }
-        vector++;
     }
 
     uart_write(dev->uart, &frame_delim, 1);
diff --git a/drivers/include/cc2420.h b/drivers/include/cc2420.h
index a2d6f77be447b320a7930a6db29f98b6b3e20f9f..b8d21767227006a896e0cd5341f41fa627739e0e 100644
--- a/drivers/include/cc2420.h
+++ b/drivers/include/cc2420.h
@@ -263,13 +263,12 @@ netopt_state_t cc2420_get_state(cc2420_t *dev);
  * @note This function ignores the PRELOADING option
  *
  * @param[in] dev           device to use for sending
- * @param[in] data          data to send (must include IEEE802.15.4 header)
- * @param[in] count         length of @p data
+ * @param[in] iolist        data to send (must include IEEE802.15.4 header)
  *
  * @return                  number of bytes that were actually send
  * @return                  0 on error
  */
-size_t cc2420_send(cc2420_t *dev, const struct iovec *data, unsigned count);
+size_t cc2420_send(cc2420_t *dev, const iolist_t *iolist);
 
 /**
  * @brief   Prepare for sending of data
@@ -278,10 +277,9 @@ size_t cc2420_send(cc2420_t *dev, const struct iovec *data, unsigned count);
  * data is possible after it was called.
  *
  * @param[in] dev           device to prepare for sending
- * @param[in] data          data to prepare (must include IEEE802.15.4 header)
- * @param[in] count         length of @p data
+ * @param[in] iolist        data to prepare (must include IEEE802.15.4 header)
  */
-size_t cc2420_tx_prepare(cc2420_t *dev, const struct iovec *data, unsigned count);
+size_t cc2420_tx_prepare(cc2420_t *dev, const iolist_t *iolist);
 
 /**
  * @brief   Trigger sending of data previously loaded into transmit buffer
diff --git a/drivers/include/net/netdev.h b/drivers/include/net/netdev.h
index 7df1c6b4ee4d7675f22ff29e1ae3e63164c3261b..9d12d232c1d7ea8fe9c00ff95bc122fb4fbd8789 100644
--- a/drivers/include/net/netdev.h
+++ b/drivers/include/net/netdev.h
@@ -195,8 +195,8 @@ extern "C" {
 #endif
 
 #include <stdint.h>
-#include <sys/uio.h>
 
+#include "iolist.h"
 #include "net/netopt.h"
 
 #ifdef MODULE_NETSTATS_L2
@@ -294,17 +294,14 @@ typedef struct netdev_driver {
     /**
      * @brief Send frame
      *
-     * @pre `(dev != NULL)`
-     * @pre `(count == 0) || (vector != NULL)`
-     *      (`(count != 0) => (vector != NULL)`)
+     * @pre `(dev != NULL) && (iolist != NULL`
      *
      * @param[in] dev       network device descriptor
-     * @param[in] vector    io vector array to send
-     * @param[in] count     nr of entries in vector
+     * @param[in] iolist    io vector list to send
      *
      * @return number of bytes sent, or `< 0` on error
      */
-    int (*send)(netdev_t *dev, const struct iovec *vector, unsigned count);
+    int (*send)(netdev_t *dev, const iolist_t *iolist);
 
     /**
      * @brief Get a received frame
diff --git a/drivers/kw2xrf/kw2xrf_netdev.c b/drivers/kw2xrf/kw2xrf_netdev.c
index 4d8a0dd3bec3f7caf4527be8dbd9504ee0f8db6e..e22ab31fdbcc6b5492175f59f04402a890b65d0b 100644
--- a/drivers/kw2xrf/kw2xrf_netdev.c
+++ b/drivers/kw2xrf/kw2xrf_netdev.c
@@ -138,10 +138,9 @@ static void kw2xrf_wait_idle(kw2xrf_t *dev)
     }
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     kw2xrf_t *dev = (kw2xrf_t *)netdev;
-    const struct iovec *ptr = vector;
     uint8_t *pkt_buf = &(dev->buf[1]);
     size_t len = 0;
 
@@ -149,14 +148,14 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
     kw2xrf_wait_idle(dev);
 
     /* load packet data into buffer */
-    for (unsigned i = 0; i < count; i++, ptr++) {
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
         /* current packet data + FCS too long */
-        if ((len + ptr->iov_len + IEEE802154_FCS_LEN) > KW2XRF_MAX_PKT_LENGTH) {
+        if ((len + iol->iol_len + IEEE802154_FCS_LEN) > KW2XRF_MAX_PKT_LENGTH) {
             LOG_ERROR("[kw2xrf] packet too large (%u byte) to be send\n",
                   (unsigned)len + IEEE802154_FCS_LEN);
             return -EOVERFLOW;
         }
-        len = kw2xrf_tx_load(pkt_buf, ptr->iov_base, ptr->iov_len, len);
+        len = kw2xrf_tx_load(pkt_buf, iol->iol_base, iol->iol_len, len);
     }
 
     kw2xrf_set_sequence(dev, XCVSEQ_IDLE);
diff --git a/drivers/mrf24j40/mrf24j40_netdev.c b/drivers/mrf24j40/mrf24j40_netdev.c
index 02598c1b4965dd2b4f4179495e7bbe69ffec500f..10f534689b771cf128a4e8ac4be617b47efc0294 100644
--- a/drivers/mrf24j40/mrf24j40_netdev.c
+++ b/drivers/mrf24j40/mrf24j40_netdev.c
@@ -39,22 +39,6 @@
 
 #define _MAX_MHR_OVERHEAD   (25)
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
-static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
-static int _init(netdev_t *netdev);
-static void _isr(netdev_t *netdev);
-static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len);
-static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len);
-
-const netdev_driver_t mrf24j40_driver = {
-    .send = _send,
-    .recv = _recv,
-    .init = _init,
-    .isr = _isr,
-    .get = _get,
-    .set = _set,
-};
-
 static void _irq_handler(void *arg)
 {
     netdev_t *dev = (netdev_t *) arg;
@@ -83,18 +67,17 @@ static int _init(netdev_t *netdev)
     return 0;
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     mrf24j40_t *dev = (mrf24j40_t *)netdev;
-    const struct iovec *ptr = vector;
     size_t len = 0;
 
     mrf24j40_tx_prepare(dev);
 
     /* load packet data into FIFO */
-    for (unsigned i = 0; i < count; i++, ptr++) {
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
         /* current packet data + FCS too long */
-        if ((len + ptr->iov_len + 2) > IEEE802154_FRAME_LEN_MAX) {
+        if ((len + iol->iol_len + 2) > IEEE802154_FRAME_LEN_MAX) {
             DEBUG("[mrf24j40] error: packet too large (%u byte) to be send\n",
                   (unsigned)len + 2);
             return -EOVERFLOW;
@@ -103,11 +86,12 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
 #ifdef MODULE_NETSTATS_L2
         netdev->stats.tx_bytes += len;
 #endif
-        len = mrf24j40_tx_load(dev, ptr->iov_base, ptr->iov_len, len);
-        if (i == 0) {
+        len = mrf24j40_tx_load(dev, iol->iol_base, iol->iol_len, len);
+        /* only on first iteration: */
+        if (iol == iolist) {
             dev->header_len = len;
             /* Grab the FCF bits from the frame header */
-            dev->fcf_low = *(uint8_t*)(ptr->iov_base);
+            dev->fcf_low = *(uint8_t*)(iol->iol_base);
         }
 
     }
@@ -581,3 +565,12 @@ static void _isr(netdev_t *netdev)
     }
     DEBUG("[mrf24j40] END IRQ\n");
 }
+
+const netdev_driver_t mrf24j40_driver = {
+    .send = _send,
+    .recv = _recv,
+    .init = _init,
+    .isr = _isr,
+    .get = _get,
+    .set = _set,
+};
diff --git a/drivers/slipdev/slipdev.c b/drivers/slipdev/slipdev.c
index 073ab4019e9a14180154207af545e07fec4a8d30..9d0a15ac3e0c86a36e332d9ea96c91f3ff424dd6 100644
--- a/drivers/slipdev/slipdev.c
+++ b/drivers/slipdev/slipdev.c
@@ -27,31 +27,6 @@
 #define SLIP_END_ESC           (0xdcU)
 #define SLIP_ESC_ESC           (0xddU)
 
-static int _send(netdev_t *dev, const struct iovec *vector, unsigned count);
-static int _recv(netdev_t *dev, void *buf, size_t len, void *info);
-static int _init(netdev_t *dev);
-static void _isr(netdev_t *dev);
-static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len);
-static int _set(netdev_t *dev, netopt_t opt, const void *value,
-                size_t value_len);
-
-static const netdev_driver_t slip_driver = {
-    .send = _send,
-    .recv = _recv,
-    .init = _init,
-    .isr = _isr,
-    .get = _get,
-    .set = _set,
-};
-
-void slipdev_setup(slipdev_t *dev, const slipdev_params_t *params)
-{
-    /* set device descriptor fields */
-    memcpy(&dev->config, params, sizeof(dev->config));
-    dev->inesc = 0U;
-    dev->netdev.driver = &slip_driver;
-}
-
 static void _slip_rx_cb(void *arg, uint8_t byte)
 {
     slipdev_t *dev = arg;
@@ -84,16 +59,16 @@ static inline void _write_byte(slipdev_t *dev, uint8_t byte)
     uart_write(dev->config.uart, &byte, 1);
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     slipdev_t *dev = (slipdev_t *)netdev;
     int bytes = 0;
 
-    DEBUG("slipdev: sending vector of length %u\n", count);
-    for (unsigned i = 0; i < count; i++) {
-        uint8_t *data = vector[i].iov_base;
+    DEBUG("slipdev: sending iolist\n");
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        uint8_t *data = iol->iol_base;
 
-        for (unsigned j = 0; j < vector[i].iov_len; j++, data++) {
+        for (unsigned j = 0; j < iol->iol_len; j++, data++) {
             switch(*data) {
                 case SLIP_END:
                     /* escaping END byte*/
@@ -223,4 +198,21 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *value,
     return -ENOTSUP;
 }
 
+static const netdev_driver_t slip_driver = {
+    .send = _send,
+    .recv = _recv,
+    .init = _init,
+    .isr = _isr,
+    .get = _get,
+    .set = _set,
+};
+
+void slipdev_setup(slipdev_t *dev, const slipdev_params_t *params)
+{
+    /* set device descriptor fields */
+    memcpy(&dev->config, params, sizeof(dev->config));
+    dev->inesc = 0U;
+    dev->netdev.driver = &slip_driver;
+}
+
 /** @} */
diff --git a/drivers/sx127x/sx127x_netdev.c b/drivers/sx127x/sx127x_netdev.c
index 30c6e7ef7313c3e8ada45d9196210ad9eda74310..60723573113d7d0adc4db36df0a694b85d193a11 100644
--- a/drivers/sx127x/sx127x_netdev.c
+++ b/drivers/sx127x/sx127x_netdev.c
@@ -34,7 +34,6 @@
 #include "debug.h"
 
 /* Internal helper functions */
-static uint8_t _get_tx_len(const struct iovec *vector, unsigned count);
 static int _set_state(sx127x_t *dev, netopt_state_t state);
 static int _get_state(sx127x_t *dev, void *val);
 void _on_dio0_irq(void *arg);
@@ -42,24 +41,7 @@ void _on_dio1_irq(void *arg);
 void _on_dio2_irq(void *arg);
 void _on_dio3_irq(void *arg);
 
-/* Netdev driver api functions */
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
-static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
-static int _init(netdev_t *netdev);
-static void _isr(netdev_t *netdev);
-static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len);
-static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len);
-
-const netdev_driver_t sx127x_driver = {
-    .send = _send,
-    .recv = _recv,
-    .init = _init,
-    .isr = _isr,
-    .get = _get,
-    .set = _set,
-};
-
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     sx127x_t *dev = (sx127x_t*) netdev;
 
@@ -69,8 +51,8 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
         return -ENOTSUP;
     }
 
-    uint8_t size;
-    size = _get_tx_len(vector, count);
+    uint8_t size = iolist_size(iolist);
+
     switch (dev->settings.modem) {
         case SX127X_MODEM_FSK:
             /* todo */
@@ -91,8 +73,8 @@ static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
             }
 
             /* Write payload buffer */
-            for (size_t i = 0; i < count; i++) {
-                sx127x_write_fifo(dev, vector[i].iov_base, vector[i].iov_len);
+            for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+                sx127x_write_fifo(dev, iol->iol_base, iol->iol_len);
             }
             break;
         default:
@@ -489,17 +471,6 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
     return res;
 }
 
-static uint8_t _get_tx_len(const struct iovec *vector, unsigned count)
-{
-    uint8_t len = 0;
-
-    for (unsigned i = 0 ; i < count ; i++) {
-        len += vector[i].iov_len;
-    }
-
-    return len;
-}
-
 static int _set_state(sx127x_t *dev, netopt_state_t state)
 {
     switch (state) {
@@ -717,3 +688,12 @@ void _on_dio3_irq(void *arg)
             break;
     }
 }
+
+const netdev_driver_t sx127x_driver = {
+    .send = _send,
+    .recv = _recv,
+    .init = _init,
+    .isr = _isr,
+    .get = _get,
+    .set = _set,
+};
diff --git a/drivers/w5100/w5100.c b/drivers/w5100/w5100.c
index 8cc6e92b7b6e0c4fc49299335a978c659e61d194..e391752fa99692aa94b6fcb9ba56e8dfdfe5088a 100644
--- a/drivers/w5100/w5100.c
+++ b/drivers/w5100/w5100.c
@@ -194,7 +194,7 @@ static uint16_t tx_upload(w5100_t *dev, uint16_t start, void *data, size_t len)
     }
 }
 
-static int send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int send(netdev_t *netdev, const iolist_t *iolist)
 {
     w5100_t *dev = (w5100_t *)netdev;
     int sum = 0;
@@ -210,9 +210,10 @@ static int send(netdev_t *netdev, const struct iovec *vector, unsigned count)
         pos = S0_TX_BASE;
     }
 
-    for (unsigned i = 0; i < count; i++) {
-        pos = tx_upload(dev, pos, vector[i].iov_base, vector[i].iov_len);
-        sum += vector[i].iov_len;
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        size_t len = iol->iol_len;
+        pos = tx_upload(dev, pos, iol->iol_base, len);
+        sum += len;
     }
 
     waddr(dev, S0_TX_WR0, S0_TX_WR1, pos);
diff --git a/drivers/xbee/gnrc_xbee.c b/drivers/xbee/gnrc_xbee.c
index be78de2aee81acf9abf4847cc0d91c74fd356e3b..8d66233c64d6551f45d648eb70a0e6f730cececa 100644
--- a/drivers/xbee/gnrc_xbee.c
+++ b/drivers/xbee/gnrc_xbee.c
@@ -104,8 +104,6 @@ static int xbee_adpt_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
 {
     int res;
     size_t size;
-    size_t count;
-    gnrc_pktsnip_t *vec;
     gnrc_netif_hdr_t *hdr;
     uint8_t xhdr[XBEE_MAX_TXHDR_LENGTH];
 
@@ -146,27 +144,23 @@ static int xbee_adpt_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
         }
     }
 
-    /* now let's extract the iovector and send out the stuff */
-    vec = gnrc_pktbuf_get_iovec(pkt, &count);
-    if (vec != NULL) {
-        pkt = vec;
-        struct iovec *vector = (struct iovec *)pkt->data;
-        vector[0].iov_base = xhdr;
-        vector[0].iov_len = res;
+    /* now let's send out the stuff */
+    iolist_t iolist = {
+        .iol_next = (iolist_t *)pkt->next,
+        .iol_base = xhdr,
+        .iol_len = res
+    };
+
 #ifdef MODULE_NETSTATS_L2
-        if (hdr->flags & BCAST) {
-            netif->dev->stats.tx_mcast_count++;
-        }
-        else {
-            netif->dev->stats.tx_unicast_count++;
-        }
-#endif
-        DEBUG("[xbee-gnrc] send: triggering the drivers send function\n");
-        res = netif->dev->driver->send(netif->dev, vector, count);
+    if (hdr->flags & BCAST) {
+        netif->dev->stats.tx_mcast_count++;
     }
     else {
-        DEBUG("[xbee-gnrc] send: unable to create iovec\n");
+        netif->dev->stats.tx_unicast_count++;
     }
+#endif
+    DEBUG("[xbee-gnrc] send: triggering the drivers send function\n");
+    res = netif->dev->driver->send(netif->dev, &iolist);
 
     gnrc_pktbuf_release(pkt);
 
diff --git a/drivers/xbee/xbee.c b/drivers/xbee/xbee.c
index 93df3ab782a459070d8e296b2e5c8000fc4c542a..fb9985896d0739c1e177a9922a2612f7b9c80291 100644
--- a/drivers/xbee/xbee.c
+++ b/drivers/xbee/xbee.c
@@ -634,21 +634,23 @@ int xbee_init(netdev_t *dev)
     return 0;
 }
 
-static int xbee_send(netdev_t *dev, const struct iovec *vector, unsigned count)
+static int xbee_send(netdev_t *dev, const iolist_t *iolist)
 {
     xbee_t *xbee = (xbee_t *)dev;
     size_t size;
     uint8_t csum;
 
-    assert(xbee && vector && (count > 0));
+    assert(xbee && iolist);
 
     /* calculate the checksum and the packet size */
-    size = vector[0].iov_len;
-    csum = _cksum(3, (uint8_t *)vector[0].iov_base, size);
-    for (unsigned i = 1; i < count; i++) {
-        size += vector[i].iov_len;
-        for (size_t p = 0; p < vector[i].iov_len; p++) {
-            csum -= ((uint8_t *)vector[i].iov_base)[p];
+    size = iolist->iol_len;
+    csum = _cksum(3, (uint8_t *)iolist->iol_base, size);
+    for (const iolist_t *iol = iolist->iol_next; iol; iol = iol->iol_next) {
+        size_t len = iol->iol_len;
+
+        size += len;
+        for (size_t p = 0; p < len; p++) {
+            csum -= ((uint8_t *)iol->iol_base)[p];
         }
     }
 
@@ -661,13 +663,13 @@ static int xbee_send(netdev_t *dev, const struct iovec *vector, unsigned count)
     /* send the actual data packet */
     DEBUG("[xbee] send: now sending out %i byte\n", (int)size);
     mutex_lock(&(xbee->tx_lock));
-    for (unsigned i = 0; i < count; i++) {
-        uart_write(xbee->p.uart, vector[i].iov_base, vector[i].iov_len);
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        uart_write(xbee->p.uart, iol->iol_base, iol->iol_len);
     }
     uart_write(xbee->p.uart, &csum, 1);
     mutex_unlock(&(xbee->tx_lock));
 
-    /* return number of payload byte */
+    /* return number of payload bytes */
     return (int)size;
 }
 
diff --git a/pkg/emb6/contrib/netdev/emb6_netdev.c b/pkg/emb6/contrib/netdev/emb6_netdev.c
index 217a2b620376b4847791d601c5dd865a270f7e56..67143c33a351b588cea8d82388ad8fb6a7539636 100644
--- a/pkg/emb6/contrib/netdev/emb6_netdev.c
+++ b/pkg/emb6/contrib/netdev/emb6_netdev.c
@@ -138,11 +138,11 @@ static int8_t _netdev_init(s_ns_t *p_ns)
 static int8_t _netdev_send(const void *pr_payload, uint8_t c_len)
 {
     if (_dev != NULL) {
-        const struct iovec vector = {
-            .iov_base = (void *)pr_payload,
-            .iov_len = c_len
+        iolist_t iolist = {
+            .iol_base = (void *)pr_payload,
+            .iol_len = c_len,
         };
-        if (_dev->driver->send(_dev, &vector, 1) < 0) {
+        if (_dev->driver->send(_dev, &iolist) < 0) {
             DEBUG("Error on send\n");
             return RADIO_TX_ERR;
         }
diff --git a/pkg/lwip/contrib/netdev/lwip_netdev.c b/pkg/lwip/contrib/netdev/lwip_netdev.c
index ffc86384419623d8814968e2fbddf66ec375aec1..cb9246aa8aa13a1558ecdda65a5fdf3a87fa0f55 100644
--- a/pkg/lwip/contrib/netdev/lwip_netdev.c
+++ b/pkg/lwip/contrib/netdev/lwip_netdev.c
@@ -185,15 +185,24 @@ static err_t _eth_link_output(struct netif *netif, struct pbuf *p)
     pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
 #endif
     LL_COUNT(p, q, count);
-    struct iovec pkt[count];
+    iolist_t iolist[count];
+
+    /* make last point to the last entry of iolist[] */
+    iolist_t *last = &iolist[count];
+    last--;
+
     for (q = p, count = 0; q != NULL; q = q->next, count++) {
-        pkt[count].iov_base = q->payload;
-        pkt[count].iov_len = (size_t)q->len;
+        iolist_t *iol = &iolist[count];
+
+        iol->iol_next = (iol == last) ? NULL : &iolist[count + 1];
+
+        iol->iol_base = q->payload;
+        iol->iol_len = (size_t)q->len;
     }
 #if ETH_PAD_SIZE
     pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
 #endif
-    return (netdev->driver->send(netdev, pkt, count) > 0) ? ERR_OK : ERR_BUF;
+    return (netdev->driver->send(netdev, iolist) > 0) ? ERR_OK : ERR_BUF;
 }
 #endif
 
@@ -202,12 +211,12 @@ static err_t _ieee802154_link_output(struct netif *netif, struct pbuf *p)
 {
     LWIP_ASSERT("p->next == NULL", p->next == NULL);
     netdev_t *netdev = (netdev_t *)netif->state;
-    struct iovec pkt = {
-        .iov_base = p->payload,
-        .iov_len = (p->len - IEEE802154_FCS_LEN),   /* FCS is written by driver */
+    iolist_t pkt = {
+        .iol_base = p->payload,
+        .iol_len = (p->len - IEEE802154_FCS_LEN),   /* FCS is written by driver */
     };
 
-    return (netdev->driver->send(netdev, &pkt, 1) > 0) ? ERR_OK : ERR_BUF;
+    return (netdev->driver->send(netdev, &pkt) > 0) ? ERR_OK : ERR_BUF;
 }
 #endif
 
diff --git a/pkg/openthread/contrib/platform_radio.c b/pkg/openthread/contrib/platform_radio.c
index 27487e36708b8516eea93aafa6b058799975d41a..8095bb658089dabfe1375442dc4d023ad8c0a6f2 100644
--- a/pkg/openthread/contrib/platform_radio.c
+++ b/pkg/openthread/contrib/platform_radio.c
@@ -297,14 +297,15 @@ void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower)
 ThreadError otPlatRadioTransmit(otInstance *aInstance, RadioPacket *aPacket)
 {
     (void) aInstance;
-    struct iovec pkt;
 
-    /* Populate iovec with transmit data
+    /* Populate iolist with transmit data
      * Unlike RIOT, OpenThread includes two bytes FCS (0x00 0x00) so
      * these bytes are removed
      */
-    pkt.iov_base = aPacket->mPsdu;
-    pkt.iov_len = aPacket->mLength - RADIO_IEEE802154_FCS_LEN;
+    iolist_t iolist = {
+        .iol_base = aPacket->mPsdu,
+        .iol_len = (aPacket->mLength - RADIO_IEEE802154_FCS_LEN)
+    };
 
     /*Set channel and power based on transmit frame */
     DEBUG("otPlatRadioTransmit->channel: %i, length %d\n", (int) aPacket->mChannel, (int)aPacket->mLength);
@@ -316,7 +317,7 @@ ThreadError otPlatRadioTransmit(otInstance *aInstance, RadioPacket *aPacket)
     _set_power(aPacket->mPower);
 
     /* send packet though netdev */
-    _dev->driver->send(_dev, &pkt, 1);
+    _dev->driver->send(_dev, &iolist);
 
     return kThreadError_None;
 }
diff --git a/pkg/semtech-loramac/contrib/semtech_loramac_radio.c b/pkg/semtech-loramac/contrib/semtech_loramac_radio.c
index 84072a5658e0adc36443d52798869a96a0b4df57..8d2d8cb8e0481b8bb8fb12f829537e99309d8779 100644
--- a/pkg/semtech-loramac/contrib/semtech_loramac_radio.c
+++ b/pkg/semtech-loramac/contrib/semtech_loramac_radio.c
@@ -136,10 +136,11 @@ uint32_t SX127XGetTimeOnAir(RadioModems_t modem, uint8_t pktLen)
 void SX127XSend(uint8_t *buffer, uint8_t size)
 {
     netdev_t *dev = (netdev_t *)&sx127x;
-    struct iovec vec[1];
-    vec[0].iov_base = buffer;
-    vec[0].iov_len = size;
-    dev->driver->send(dev, vec, 1);
+    iolist_t iol = {
+        .iol_base = buffer,
+        .iol_len = size
+    };
+    dev->driver->send(dev, &iol);
 }
 
 void SX127XSetSleep(void)
diff --git a/sys/include/iolist.h b/sys/include/iolist.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5d8d753e75cc62b3cf1203f76d12a56362874c2
--- /dev/null
+++ b/sys/include/iolist.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup    sys_util_iolist iolist scatter / gather IO
+ * @ingroup     sys_util
+ * @brief       Provides linked-list scatter / gather IO
+ *
+ * @{
+ *
+ * @file
+ * @brief       iolist scatter / gather IO
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ */
+
+#ifndef IOLIST_H
+#define IOLIST_H
+
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @brief iolist forward declaration */
+typedef struct iolist iolist_t;
+
+/**
+ * @brief   iolist structure definition
+ */
+struct iolist {
+    iolist_t *iol_next;     /**< ptr to next list entry */
+    void *iol_base;         /**< ptr to this list entries data */
+    size_t iol_len;         /**< size of data pointet to by ptr */
+};
+
+/**
+ * @brief   Count number of entries in an iolist_t
+ *
+ * @param[in]   iolist  iolist to count
+ *
+ * @returns number of entries (zero for NULL parameter)
+ */
+unsigned iolist_count(const iolist_t *iolist);
+
+/**
+ * @brief   Sum up number of bytes in iolist
+ *
+ * This function returns the summed ip lenght values of all entries in @p
+ * iolist.
+ *
+ * @param[in]   iolist  iolist to sum up
+ *
+ * @returns summed up number of bytes or zero if @p iolist == NULL
+ */
+size_t iolist_size(const iolist_t *iolist);
+
+/** @brief  struct iovec anonymous declaration */
+struct iovec;
+
+/**
+ * @brief   Create struct iovec from iolist
+ *
+ * This function fills an array of struct iovecs with the contents of @p
+ * iolist. It will write the number of used array entries into @p count.
+ *
+ * The caller *must* ensure that @p iov p points to an array of size >= count!
+ *
+ * @param[in]   iolist  iolist to read from
+ * @param[out]  iov     ptr to array of struct iovec that will be filled
+ * @param[out]  count   number of elements in @p iolist
+ *
+ * @returns iolist_size(iolist)
+ */
+size_t iolist_to_iovec(const iolist_t *iolist, struct iovec *iov, unsigned *count);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* IOLIST_H */
+/** @} */
diff --git a/sys/include/net/csma_sender.h b/sys/include/net/csma_sender.h
index 0928215f780f860368ffec79a80693a2b4402546..b7183c0325890c494b684b54bb1fc74999d87e32 100644
--- a/sys/include/net/csma_sender.h
+++ b/sys/include/net/csma_sender.h
@@ -87,8 +87,7 @@ extern const csma_sender_conf_t CSMA_SENDER_CONF_DEFAULT;
  * CSMA/CA, this feature is used. Otherwise, a software procedure is used.
  *
  * @param[in] dev       netdev device, needs to be already initialized
- * @param[in] vector    pointer to the data
- * @param[in] count     number of elements in @p vector
+ * @param[in] iolist    pointer to the data
  * @param[in] conf      configuration for the backoff;
  *                      will be set to @ref CSMA_SENDER_CONF_DEFAULT if NULL.
  *
@@ -101,8 +100,8 @@ extern const csma_sender_conf_t CSMA_SENDER_CONF_DEFAULT;
  * @return              -EBUSY if radio medium never was available
  *                      to send the given data
  */
-int csma_sender_csma_ca_send(netdev_t *dev, struct iovec *vector,
-                             unsigned count, const csma_sender_conf_t *conf);
+int csma_sender_csma_ca_send(netdev_t *dev, iolist_t *iolist,
+                             const csma_sender_conf_t *conf);
 
 /**
  * @brief   Sends a 802.15.4 frame when medium is avaiable.
@@ -121,8 +120,7 @@ int csma_sender_csma_ca_send(netdev_t *dev, struct iovec *vector,
  *          @ref csma_sender_csma_ca_send().
  *
  * @param[in] dev       netdev device, needs to be already initialized
- * @param[in] vector    pointer to the data
- * @param[in] count     number of elements in @p vector
+ * @param[in] iolist    pointer to the data
  *
  * @return              number of bytes that were actually send out
  * @return              -ENODEV if @p dev is invalid
@@ -133,7 +131,7 @@ int csma_sender_csma_ca_send(netdev_t *dev, struct iovec *vector,
  * @return              -EBUSY if radio medium was not available
  *                      to send the given data
  */
-int csma_sender_cca_send(netdev_t *dev, struct iovec *vector, unsigned count);
+int csma_sender_cca_send(netdev_t *dev, iolist_t *iolist);
 
 
 #ifdef __cplusplus
diff --git a/sys/include/net/gnrc/pkt.h b/sys/include/net/gnrc/pkt.h
index 647d8ca776b4f83a60389bc010909fa0c3eb848f..07eb593c4cbea0a143ef25847c0237b7fbbd7b6e 100644
--- a/sys/include/net/gnrc/pkt.h
+++ b/sys/include/net/gnrc/pkt.h
@@ -96,20 +96,25 @@ extern "C" {
  *      | * L2 header 3
  *      * L2 header 4
  *
+ * The first three fields (next, data, size) match iolist_t (named iol_next,
+ * iol_base and iol_len there).  That means that any pktsnip can be casted to
+ * iolist_t for direct passing to e.g., netdev send() functions.
+ *
  * @note    This type has no initializer on purpose. Please use @ref net_gnrc_pktbuf
  *          as factory.
  */
 /* packed to be aligned correctly in the static packet buffer */
 typedef struct gnrc_pktsnip {
+    /* the first three fields *MUST* match iolist_t! */
+    struct gnrc_pktsnip *next;      /**< next snip in the packet */
+    void *data;                     /**< pointer to the data of the snip */
+    size_t size;                    /**< the length of the snip in byte */
     /**
      * @brief   Counter of threads currently having control over this packet.
      *
      * @internal
      */
     unsigned int users;
-    struct gnrc_pktsnip *next;      /**< next snip in the packet */
-    void *data;                     /**< pointer to the data of the snip */
-    size_t size;                    /**< the length of the snip in byte */
     gnrc_nettype_t type;            /**< protocol of the packet snip */
 #ifdef MODULE_GNRC_NETERR
     kernel_pid_t err_sub;           /**< subscriber to errors related to this
diff --git a/sys/include/net/netdev_test.h b/sys/include/net/netdev_test.h
index 016db2d55bb107a2b2b494e59b56d383f54f7c19..6260b610eeb752096e3acb68a9ab5b60f27783df 100644
--- a/sys/include/net/netdev_test.h
+++ b/sys/include/net/netdev_test.h
@@ -33,11 +33,10 @@
  * static uint32_t sum = 0;
  * static mutex_t wait = MUTEX_INIT;
  *
- * int _send_timer(netdev_t *dev, const struct iovec *vector, int count)
+ * int _send_timer(netdev_t *dev, const iolist_t *iolist)
  * {
  *     (void)dev;
- *     (void)vector;
- *     (void)count;
+ *     (void)iolist;
  *
  *     sum += (xtimer_now_usec() - last_start);
  *     mutex_unlock(&wait);
@@ -95,15 +94,12 @@ extern "C" {
  * @brief   Callback type to handle send command
  *
  * @param[in] dev       network device descriptor
- * @param[in] vector    io vector array to send
- * @param[in] count     number of entries in vector
+ * @param[in] iolist    io vector list to send
  *
  * @return  number of bytes sent
  * @return  <= 0 on error
  */
-typedef int (*netdev_test_send_cb_t)(netdev_t *dev,
-                                     const struct iovec *vector,
-                                     int count);
+typedef int (*netdev_test_send_cb_t)(netdev_t *dev, const iolist_t *iolist);
 
 /**
  * @brief   Callback type to handle receive command
diff --git a/sys/iolist/Makefile b/sys/iolist/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2
--- /dev/null
+++ b/sys/iolist/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/iolist/iolist.c b/sys/iolist/iolist.c
new file mode 100644
index 0000000000000000000000000000000000000000..b64f0139e251d7fbc72fcb1cbd7c0127f28e8b9b
--- /dev/null
+++ b/sys/iolist/iolist.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
+ *
+ * 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     sys_util
+ * @{
+ *
+ * @file
+ * @brief       iolist scatter / gather IO
+ *
+ * @author      Kaspar Schleiser <kaspar@schleiser.de>
+ * @}
+ */
+
+#include <sys/uio.h>
+
+#include "iolist.h"
+
+unsigned iolist_count(const iolist_t *iolist)
+{
+    unsigned count = 0;
+    while (iolist) {
+        count++;
+        iolist = iolist->iol_next;
+    }
+    return count;
+}
+
+size_t iolist_size(const iolist_t *iolist)
+{
+    size_t result = 0;
+    while (iolist) {
+        result += iolist->iol_len;
+        iolist = iolist->iol_next;
+    }
+    return result;
+}
+
+size_t iolist_to_iovec(const iolist_t *iolist, struct iovec *iov, unsigned *count)
+{
+    size_t bytes = 0;
+    unsigned _count = 0;
+
+    while (iolist) {
+        iov->iov_base = iolist->iol_base;
+        iov->iov_len = iolist->iol_len;
+        bytes += iov->iov_len;
+        _count++;
+        iolist = iolist->iol_next;
+        iov++;
+    }
+
+    *count = _count;
+
+    return bytes;
+}
diff --git a/sys/net/gnrc/link_layer/gomach/gomach_internal.c b/sys/net/gnrc/link_layer/gomach/gomach_internal.c
index 3b30d4fb67292ef3e03fe2c82c516047485c9143..3b6618324509a85ea2f1338a81a8744494fce8bb 100644
--- a/sys/net/gnrc/link_layer/gomach/gomach_internal.c
+++ b/sys/net/gnrc/link_layer/gomach/gomach_internal.c
@@ -50,10 +50,9 @@ int _gnrc_gomach_transmit(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
     netdev_t *dev = netif->dev;
     netdev_ieee802154_t *state = (netdev_ieee802154_t *)netif->dev;
     gnrc_netif_hdr_t *netif_hdr;
-    gnrc_pktsnip_t *vec_snip;
     const uint8_t *src, *dst = NULL;
     int res = 0;
-    size_t n, src_len, dst_len;
+    size_t src_len, dst_len;
     uint8_t mhr[IEEE802154_MAX_HDR_LEN];
     uint8_t flags = (uint8_t)(state->flags & NETDEV_IEEE802154_SEND_MASK);
     le_uint16_t dev_pan = byteorder_btols(byteorder_htons(state->pan));
@@ -93,38 +92,34 @@ int _gnrc_gomach_transmit(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
         DEBUG("_send_ieee802154: Error preperaring frame\n");
         return -EINVAL;
     }
+
     /* prepare packet for sending */
-    vec_snip = gnrc_pktbuf_get_iovec(pkt, &n);
-    if (vec_snip != NULL) {
-        struct iovec *vector;
-
-        pkt = vec_snip;     /* reassign for later release; vec_snip is prepended to pkt */
-        vector = (struct iovec *)pkt->data;
-        vector[0].iov_base = mhr;
-        vector[0].iov_len = (size_t)res;
+    iolist_t iolist = {
+        .iol_next = (iolist_t *)pkt->next,
+        .iol_base = mhr,
+        .iol_len = (size_t)res
+    };
+
 #ifdef MODULE_NETSTATS_L2
-        if (netif_hdr->flags &
+    if (netif_hdr->flags &
             (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
-            netif->dev->stats.tx_mcast_count++;
-        }
-        else {
-            netif->dev->stats.tx_unicast_count++;
-        }
+        netif->dev->stats.tx_mcast_count++;
+    }
+    else {
+        netif->dev->stats.tx_unicast_count++;
+    }
 #endif
 #ifdef MODULE_GNRC_MAC
-        if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) {
-            res = csma_sender_csma_ca_send(dev, vector, n, &netif->mac.csma_conf);
-        }
-        else {
-            res = dev->driver->send(dev, vector, n);
-        }
-#else
-        res = dev->driver->send(dev, vector, n);
-#endif
+    if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) {
+        res = csma_sender_csma_ca_send(dev, &iolist, &netif->mac.csma_conf);
     }
     else {
-        return -ENOBUFS;
+        res = dev->driver->send(dev, &iolist);
     }
+#else
+    res = dev->driver->send(dev, &iolist);
+#endif
+
     /* release old data */
     gnrc_pktbuf_release(pkt);
     return res;
diff --git a/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c b/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
index a4668e746ca136d292eaac339b69c139ced8a9d1..181b1c029719b94028a79acf7d60a5eb8768f716 100644
--- a/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
+++ b/sys/net/gnrc/link_layer/lwmac/lwmac_internal.c
@@ -37,10 +37,9 @@ int _gnrc_lwmac_transmit(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
     netdev_t *dev = netif->dev;
     netdev_ieee802154_t *state = (netdev_ieee802154_t *)netif->dev;
     gnrc_netif_hdr_t *netif_hdr;
-    gnrc_pktsnip_t *vec_snip;
     const uint8_t *src, *dst = NULL;
     int res = 0;
-    size_t n, src_len, dst_len;
+    size_t src_len, dst_len;
     uint8_t mhr[IEEE802154_MAX_HDR_LEN];
     uint8_t flags = (uint8_t)(state->flags & NETDEV_IEEE802154_SEND_MASK);
     le_uint16_t dev_pan = byteorder_btols(byteorder_htons(state->pan));
@@ -80,38 +79,34 @@ int _gnrc_lwmac_transmit(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
         DEBUG("_send_ieee802154: Error preperaring frame\n");
         return -EINVAL;
     }
+
     /* prepare packet for sending */
-    vec_snip = gnrc_pktbuf_get_iovec(pkt, &n);
-    if (vec_snip != NULL) {
-        struct iovec *vector;
+    iolist_t iolist = {
+        .iol_next = (iolist_t *)pkt->next,
+        .iol_base = mhr,
+        .iol_len = (size_t)res
+    };
 
-        pkt = vec_snip;     /* reassign for later release; vec_snip is prepended to pkt */
-        vector = (struct iovec *)pkt->data;
-        vector[0].iov_base = mhr;
-        vector[0].iov_len = (size_t)res;
 #ifdef MODULE_NETSTATS_L2
-        if (netif_hdr->flags &
+    if (netif_hdr->flags &
             (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
-            netif->dev->stats.tx_mcast_count++;
-        }
-        else {
-            netif->dev->stats.tx_unicast_count++;
-        }
+        netif->dev->stats.tx_mcast_count++;
+    }
+    else {
+        netif->dev->stats.tx_unicast_count++;
+    }
 #endif
 #ifdef MODULE_GNRC_MAC
-        if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) {
-            res = csma_sender_csma_ca_send(dev, vector, n, &netif->mac.csma_conf);
-        }
-        else {
-            res = dev->driver->send(dev, vector, n);
-        }
-#else
-        res = dev->driver->send(dev, vector, n);
-#endif
+    if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) {
+        res = csma_sender_csma_ca_send(dev, &iolist, &netif->mac.csma_conf);
     }
     else {
-        return -ENOBUFS;
+        res = dev->driver->send(dev, &iolist);
     }
+#else
+    res = dev->driver->send(dev, &iolist);
+#endif
+
     /* release old data */
     gnrc_pktbuf_release(pkt);
     return res;
diff --git a/sys/net/gnrc/netif/gnrc_netif_ethernet.c b/sys/net/gnrc/netif/gnrc_netif_ethernet.c
index a5b0be4038c4b4b67f4e8f7a1d3d8a580c9aac8a..7aafc78808346bd52b92159b1981791e58e37caf 100644
--- a/sys/net/gnrc/netif/gnrc_netif_ethernet.c
+++ b/sys/net/gnrc/netif/gnrc_netif_ethernet.c
@@ -135,26 +135,22 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
           hdr.dst[0], hdr.dst[1], hdr.dst[2],
           hdr.dst[3], hdr.dst[4], hdr.dst[5]);
 
-    size_t n;
-    payload = gnrc_pktbuf_get_iovec(pkt, &n);   /* use payload as temporary
-                                                 * variable */
-    res = -ENOBUFS;
-    if (payload != NULL) {
-        pkt = payload;      /* reassign for later release; vec_snip is prepended to pkt */
-        struct iovec *vector = (struct iovec *)pkt->data;
-        vector[0].iov_base = (char *)&hdr;
-        vector[0].iov_len = sizeof(ethernet_hdr_t);
+    iolist_t iolist = {
+        .iol_next = (iolist_t *)payload,
+        .iol_base = &hdr,
+        .iol_len = sizeof(ethernet_hdr_t)
+    };
+
 #ifdef MODULE_NETSTATS_L2
-        if ((netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_BROADCAST) ||
-            (netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
-            dev->stats.tx_mcast_count++;
-        }
-        else {
-            dev->stats.tx_unicast_count++;
-        }
-#endif
-        res = dev->driver->send(dev, vector, n);
+    if ((netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_BROADCAST) ||
+        (netif_hdr->flags & GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
+        dev->stats.tx_mcast_count++;
     }
+    else {
+        dev->stats.tx_unicast_count++;
+    }
+#endif
+    res = dev->driver->send(dev, &iolist);
 
     gnrc_pktbuf_release(pkt);
 
diff --git a/sys/net/gnrc/netif/gnrc_netif_ieee802154.c b/sys/net/gnrc/netif/gnrc_netif_ieee802154.c
index a482c64c1154708c10f38e6d318b49194fd9de27..f72f6ff1d4849c40288aa27c467a6c9ec652bf10 100644
--- a/sys/net/gnrc/netif/gnrc_netif_ieee802154.c
+++ b/sys/net/gnrc/netif/gnrc_netif_ieee802154.c
@@ -168,10 +168,9 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
     netdev_t *dev = netif->dev;
     netdev_ieee802154_t *state = (netdev_ieee802154_t *)netif->dev;
     gnrc_netif_hdr_t *netif_hdr;
-    gnrc_pktsnip_t *vec_snip;
     const uint8_t *src, *dst = NULL;
     int res = 0;
-    size_t n, src_len, dst_len;
+    size_t src_len, dst_len;
     uint8_t mhr[IEEE802154_MAX_HDR_LEN];
     uint8_t flags = (uint8_t)(state->flags & NETDEV_IEEE802154_SEND_MASK);
     le_uint16_t dev_pan = byteorder_btols(byteorder_htons(state->pan));
@@ -211,38 +210,34 @@ static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
         DEBUG("_send_ieee802154: Error preperaring frame\n");
         return -EINVAL;
     }
-    /* prepare packet for sending */
-    vec_snip = gnrc_pktbuf_get_iovec(pkt, &n);
-    if (vec_snip != NULL) {
-        struct iovec *vector;
-
-        pkt = vec_snip;     /* reassign for later release; vec_snip is prepended to pkt */
-        vector = (struct iovec *)pkt->data;
-        vector[0].iov_base = mhr;
-        vector[0].iov_len = (size_t)res;
+
+    /* prepare iolist for netdev / mac layer */
+    iolist_t iolist = {
+        .iol_next = (iolist_t *)pkt->next,
+        .iol_base = mhr,
+        .iol_len = (size_t)res
+    };
+
 #ifdef MODULE_NETSTATS_L2
     if (netif_hdr->flags &
-        (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
-            netif->dev->stats.tx_mcast_count++;
-        }
-        else {
-            netif->dev->stats.tx_unicast_count++;
-        }
+            (GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
+        netif->dev->stats.tx_mcast_count++;
+    }
+    else {
+        netif->dev->stats.tx_unicast_count++;
+    }
 #endif
 #ifdef MODULE_GNRC_MAC
-        if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) {
-            res = csma_sender_csma_ca_send(dev, vector, n, &netif->mac.csma_conf);
-        }
-        else {
-            res = dev->driver->send(dev, vector, n);
-        }
-#else
-        res = dev->driver->send(dev, vector, n);
-#endif
+    if (netif->mac.mac_info & GNRC_NETIF_MAC_INFO_CSMA_ENABLED) {
+        res = csma_sender_csma_ca_send(dev, &iolist, &netif->mac.csma_conf);
     }
     else {
-        return -ENOBUFS;
+        res = dev->driver->send(dev, &iolist);
     }
+#else
+    res = dev->driver->send(dev, &iolist);
+#endif
+
     /* release old data */
     gnrc_pktbuf_release(pkt);
     return res;
diff --git a/sys/net/gnrc/netif/gnrc_netif_raw.c b/sys/net/gnrc/netif/gnrc_netif_raw.c
index 53be2147cf1d19de00d90b98cf62e6b24ad61ab7..cc8760b4ce7cef2c5cdb23af59b03526c0f3ac30 100644
--- a/sys/net/gnrc/netif/gnrc_netif_raw.c
+++ b/sys/net/gnrc/netif/gnrc_netif_raw.c
@@ -92,27 +92,20 @@ static gnrc_pktsnip_t *_recv(gnrc_netif_t *netif)
 
 static int _send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
 {
-    gnrc_pktsnip_t *vector;
     int res = -ENOBUFS;
-    size_t n;
 
     if (pkt->type == GNRC_NETTYPE_NETIF) {
         /* we don't need the netif snip: remove it */
         pkt = gnrc_pktbuf_remove_snip(pkt, pkt);
     }
-    /* prepare packet for sending */
-    vector = gnrc_pktbuf_get_iovec(pkt, &n);
-    if (vector != NULL) {
-        /* reassign for later release; vector is prepended to pkt */
-        pkt = vector;
-        struct iovec *v = (struct iovec *)vector->data;
-        netdev_t *dev = netif->dev;
+
+    netdev_t *dev = netif->dev;
 
 #ifdef MODULE_NETSTATS_L2
-        dev->stats.tx_unicast_count++;
+    dev->stats.tx_unicast_count++;
 #endif
-        res = dev->driver->send(dev, v, n);
-    }
+
+    res = dev->driver->send(dev, (iolist_t *)pkt);
     /* release old data */
     gnrc_pktbuf_release(pkt);
     return res;
diff --git a/sys/net/link_layer/csma_sender/csma_sender.c b/sys/net/link_layer/csma_sender/csma_sender.c
index 1530f7832e3f5afe8bce6f95fb731cd7c9e99806..49c470ab5eeee8f5cc6ce032e3e428f1919eba8b 100644
--- a/sys/net/link_layer/csma_sender/csma_sender.c
+++ b/sys/net/link_layer/csma_sender/csma_sender.c
@@ -78,8 +78,7 @@ static inline uint32_t choose_backoff_period(int be,
  * @brief Perform a CCA and send the given packet if medium is available
  *
  * @param[in] device    netdev device, needs to be already initialized
- * @param[in] vector    pointer to the data
- * @param[in] count     number of elements in @p vector
+ * @param[in] iolist    pointer to the data
  *
  * @return              the return value of device driver's
  *                      netdev_driver_t::send() function if medium was
@@ -88,7 +87,7 @@ static inline uint32_t choose_backoff_period(int be,
  * @return              -EBUSY if radio medium was not available
  *                      to send the given data
  */
-static int send_if_cca(netdev_t *device, struct iovec *vector, unsigned count)
+static int send_if_cca(netdev_t *device, iolist_t *iolist)
 {
     netopt_enable_t hwfeat;
 
@@ -107,7 +106,7 @@ static int send_if_cca(netdev_t *device, struct iovec *vector, unsigned count)
     /* if medium is clear, send the packet and return */
     if (hwfeat == NETOPT_ENABLE) {
         DEBUG("csma: Radio medium available: sending packet.\n");
-        return device->driver->send(device, vector, count);
+        return device->driver->send(device, iolist);
     }
 
     /* if we arrive here, medium was not available for transmission */
@@ -117,8 +116,8 @@ static int send_if_cca(netdev_t *device, struct iovec *vector, unsigned count)
 
 /*------------------------- "EXPORTED" FUNCTIONS -------------------------*/
 
-int csma_sender_csma_ca_send(netdev_t *dev, struct iovec *vector,
-                             unsigned count, const csma_sender_conf_t *conf)
+int csma_sender_csma_ca_send(netdev_t *dev, iolist_t *iolist,
+                             const csma_sender_conf_t *conf)
 {
     netopt_enable_t hwfeat;
 
@@ -153,7 +152,7 @@ int csma_sender_csma_ca_send(netdev_t *dev, struct iovec *vector,
     if (ok) {
         /* device does CSMA/CA all by itself: let it do its job */
         DEBUG("csma: Network device does hardware CSMA/CA\n");
-        return dev->driver->send(dev, vector, count);
+        return dev->driver->send(dev, iolist);
     }
 
     /* if we arrive here, then we must perform the CSMA/CA procedure
@@ -169,7 +168,7 @@ int csma_sender_csma_ca_send(netdev_t *dev, struct iovec *vector,
         xtimer_usleep(bp);
 
         /* try to send after a CCA */
-        res = send_if_cca(dev, vector, count);
+        res = send_if_cca(dev, iolist);
         if (res >= 0) {
             /* TX done */
             return res;
@@ -195,7 +194,7 @@ int csma_sender_csma_ca_send(netdev_t *dev, struct iovec *vector,
 }
 
 
-int csma_sender_cca_send(netdev_t *dev, struct iovec *vector, unsigned count)
+int csma_sender_cca_send(netdev_t *dev, iolist_t *iolist)
 {
     netopt_enable_t hwfeat;
 
@@ -226,12 +225,12 @@ int csma_sender_cca_send(netdev_t *dev, struct iovec *vector, unsigned count)
     if (ok) {
         /* device does auto-CCA: let him do its job */
         DEBUG("csma: Network device does auto-CCA checking.\n");
-        return dev->driver->send(dev, vector, count);
+        return dev->driver->send(dev, iolist);
     }
 
     /* if we arrive here, we must do CCA ourselves to see if radio medium
        is clear before sending */
-    res = send_if_cca(dev, vector, count);
+    res = send_if_cca(dev, iolist);
     if (res == -EBUSY) {
         DEBUG("csma: Transmission cancelled!\n");
     }
diff --git a/sys/net/netdev_test/netdev_test.c b/sys/net/netdev_test/netdev_test.c
index 0dba17a6b704f5805fa65eef046e769f864ce36b..69883c1be2b62e3e221d43fb8e5e1b821c7bc3d2 100644
--- a/sys/net/netdev_test/netdev_test.c
+++ b/sys/net/netdev_test/netdev_test.c
@@ -19,32 +19,6 @@
 
 #include "net/netdev_test.h"
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count);
-static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
-static int _init(netdev_t *dev);
-static void _isr(netdev_t *dev);
-static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len);
-static int _set(netdev_t *dev, netopt_t opt, const void *value, size_t value_len);
-
-static const netdev_driver_t _driver = {
-    .send   = _send,
-    .recv   = _recv,
-    .init   = _init,
-    .isr    = _isr,
-    .get    = _get,
-    .set    = _set,
-};
-
-void netdev_test_setup(netdev_test_t *dev, void *state)
-{
-    netdev_t *netdev = (netdev_t *)dev;
-
-    netdev->driver = &_driver;
-    dev->state = state;
-    mutex_init(&dev->mutex);
-    netdev_test_reset(dev);
-}
-
 void netdev_test_reset(netdev_test_t *dev)
 {
     mutex_lock(&dev->mutex);
@@ -57,14 +31,14 @@ void netdev_test_reset(netdev_test_t *dev)
     mutex_unlock(&dev->mutex);
 }
 
-static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
+static int _send(netdev_t *netdev, const iolist_t *iolist)
 {
     netdev_test_t *dev = (netdev_test_t *)netdev;
-    int res = (int)count;   /* assume everything would be fine */
+    int res = -EINVAL;
 
     mutex_lock(&dev->mutex);
     if (dev->send_cb != NULL) {
-        res = dev->send_cb(netdev, vector, count);
+        res = dev->send_cb(netdev, iolist);
     }
     mutex_unlock(&dev->mutex);
     return res;
@@ -140,5 +114,23 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *value, size_t value_
     return res;
 }
 
+static const netdev_driver_t _driver = {
+    .send   = _send,
+    .recv   = _recv,
+    .init   = _init,
+    .isr    = _isr,
+    .get    = _get,
+    .set    = _set,
+};
+
+void netdev_test_setup(netdev_test_t *dev, void *state)
+{
+    netdev_t *netdev = (netdev_t *)dev;
+
+    netdev->driver = &_driver;
+    dev->state = state;
+    mutex_init(&dev->mutex);
+    netdev_test_reset(dev);
+}
 
 /** @} */
diff --git a/tests/driver_at86rf2xx/cmd.c b/tests/driver_at86rf2xx/cmd.c
index 58c34630804d3674675fc94a1bc6db113517a12f..6105cfe95cbe13deed0c7d89f0f5f82c9d281a9b 100644
--- a/tests/driver_at86rf2xx/cmd.c
+++ b/tests/driver_at86rf2xx/cmd.c
@@ -250,7 +250,6 @@ static int send(int iface, le_uint16_t dst_pan, uint8_t *dst, size_t dst_len,
 {
     int res;
     netdev_ieee802154_t *dev;
-    struct iovec vector[MAC_VECTOR_SIZE];
     uint8_t *src;
     size_t src_len;
     uint8_t mhr[IEEE802154_MAX_HDR_LEN];
@@ -262,11 +261,14 @@ static int send(int iface, le_uint16_t dst_pan, uint8_t *dst, size_t dst_len,
         return 1;
     }
 
+    iolist_t iol_data = {
+        .iol_base = data,
+        .iol_len = strlen(data)
+    };
+
     dev = (netdev_ieee802154_t *)&devs[iface];
     flags = (uint8_t)(dev->flags & NETDEV_IEEE802154_SEND_MASK);
     flags |= IEEE802154_FCF_TYPE_DATA;
-    vector[1].iov_base = data;
-    vector[1].iov_len = strlen(data);
     src_pan = byteorder_btols(byteorder_htons(dev->pan));
     if (dst_pan.u16 == 0) {
         dst_pan = src_pan;
@@ -287,15 +289,20 @@ static int send(int iface, le_uint16_t dst_pan, uint8_t *dst, size_t dst_len,
         puts("txtsnd: Error preperaring frame");
         return 1;
     }
-    vector[0].iov_base = mhr;
-    vector[0].iov_len = (size_t)res;
-    res = dev->netdev.driver->send((netdev_t *)dev, vector, MAC_VECTOR_SIZE);
+
+    iolist_t iol_hdr = {
+        .iol_next = &iol_data,
+        .iol_base = mhr,
+        .iol_len = (size_t)res
+    };
+
+    res = dev->netdev.driver->send((netdev_t *)dev, &iol_hdr);
     if (res < 0) {
         puts("txtsnd: Error on sending");
         return 1;
     }
     else {
-        printf("txtsnd: send %u bytes to ", (unsigned)vector[1].iov_len);
+        printf("txtsnd: send %u bytes to ", (unsigned)iol_data.iol_len);
         print_addr(dst, dst_len);
         printf(" (PAN: ");
         print_addr((uint8_t *)&dst_pan, sizeof(dst_pan));
diff --git a/tests/driver_sx127x/main.c b/tests/driver_sx127x/main.c
index e978cf4e1f26e0d071244ef5968f3a0b83df3fa6..961c145edf2eaa4df72b11a1f57ad54b3d23ef9e 100644
--- a/tests/driver_sx127x/main.c
+++ b/tests/driver_sx127x/main.c
@@ -224,10 +224,12 @@ int send_cmd(int argc, char **argv)
     printf("sending \"%s\" payload (%d bytes)\n",
            argv[1], strlen(argv[1]) + 1);
 
-    struct iovec vec[1];
-    vec[0].iov_base = argv[1];
-    vec[0].iov_len = strlen(argv[1]) + 1;
-    if (netdev->driver->send(netdev, vec, 1) == -ENOTSUP) {
+    iolist_t iolist = {
+        .iol_base = argv[1],
+        .iol_len = (strlen(argv[1]) + 1)
+    };
+
+    if (netdev->driver->send(netdev, &iolist) == -ENOTSUP) {
         puts("Cannot send: radio is still transmitting");
     }
 
diff --git a/tests/gnrc_netif/common.c b/tests/gnrc_netif/common.c
index 7d0bf93ce08dc380a42fc3171ebc30f033299c8a..5a29d29391982954e473bda7669f0c1c5c2fb6b2 100644
--- a/tests/gnrc_netif/common.c
+++ b/tests/gnrc_netif/common.c
@@ -37,8 +37,7 @@ static msg_t _main_msg_queue[MSG_QUEUE_SIZE];
 static uint8_t tmp_buffer[ETHERNET_DATA_LEN];
 static size_t tmp_buffer_bytes = 0;
 
-static int _dump_send_packet(netdev_t *netdev, const struct iovec *vector,
-                             int count)
+static int _dump_send_packet(netdev_t *netdev, const iolist_t *iolist)
 {
     int res;
 
@@ -55,13 +54,13 @@ static int _dump_send_packet(netdev_t *netdev, const struct iovec *vector,
         printf("unknown ");
     }
     puts("device:");
-    for (int i = 0; i < count; i++) {
-        if ((tmp_buffer_bytes + vector[i].iov_len) > ETHERNET_DATA_LEN) {
+    for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
+        size_t len = iol->iol_len;
+        if ((tmp_buffer_bytes + len) > ETHERNET_DATA_LEN) {
             return -ENOBUFS;
         }
-        memcpy(&tmp_buffer[tmp_buffer_bytes], vector[i].iov_base,
-               vector[i].iov_len);
-        tmp_buffer_bytes += vector[i].iov_len;
+        memcpy(&tmp_buffer[tmp_buffer_bytes], iol->iol_base, len);
+        tmp_buffer_bytes += len;
     }
     od_hex_dump(tmp_buffer, tmp_buffer_bytes, OD_WIDTH_DEFAULT);
     res = (int)tmp_buffer_bytes;
diff --git a/tests/lwip_sock_ip/stack.c b/tests/lwip_sock_ip/stack.c
index 52e58a3f526db4c7aee55018e55940d27810d592..23ee9cabf99ac891c059f85de1ae4fb9b3568eed 100644
--- a/tests/lwip_sock_ip/stack.c
+++ b/tests/lwip_sock_ip/stack.c
@@ -141,16 +141,16 @@ static int _netdev_recv(netdev_t *dev, char *buf, int len, void *info)
     return res;
 }
 
-static int _netdev_send(netdev_t *dev, const struct iovec *vector, int count)
+static int _netdev_send(netdev_t *dev, const iolist_t *iolist)
 {
     msg_t done = { .type = _SEND_DONE };
     unsigned offset = 0;
 
     (void)dev;
     mutex_lock(&_netdev_buffer_mutex);
-    for (int i = 0; i < count; i++) {
-        memcpy(&_netdev_buffer[offset], vector[i].iov_base, vector[i].iov_len);
-        offset += vector[i].iov_len;
+    for (; iolist; iolist = iolist->iol_next) {
+        memcpy(&_netdev_buffer[offset], iolist->iol_base, iolist->iol_len);
+        offset += iolist->iol_len;
         if (offset > sizeof(_netdev_buffer)) {
             mutex_unlock(&_netdev_buffer_mutex);
             return -ENOBUFS;
diff --git a/tests/lwip_sock_udp/stack.c b/tests/lwip_sock_udp/stack.c
index 45539cb478e7a10c45c764231e6a0c94167f656c..cdde693b293c0b09bb378892d66256684aaa2687 100644
--- a/tests/lwip_sock_udp/stack.c
+++ b/tests/lwip_sock_udp/stack.c
@@ -143,16 +143,16 @@ static int _netdev_recv(netdev_t *dev, char *buf, int len, void *info)
     return res;
 }
 
-static int _netdev_send(netdev_t *dev, const struct iovec *vector, int count)
+static int _netdev_send(netdev_t *dev, const iolist_t *iolist)
 {
     msg_t done = { .type = _SEND_DONE };
     unsigned offset = 0;
 
     (void)dev;
     mutex_lock(&_netdev_buffer_mutex);
-    for (int i = 0; i < count; i++) {
-        memcpy(&_netdev_buffer[offset], vector[i].iov_base, vector[i].iov_len);
-        offset += vector[i].iov_len;
+    for (; iolist; iolist = iolist->iol_next) {
+        memcpy(&_netdev_buffer[offset], iolist->iol_base, iolist->iol_len);
+        offset += iolist->iol_len;
         if (offset > sizeof(_netdev_buffer)) {
             mutex_unlock(&_netdev_buffer_mutex);
             return -ENOBUFS;
diff --git a/tests/netdev_test/main.c b/tests/netdev_test/main.c
index ae1bb37e3be659622cc6b094c657b50079403e96..2ba913765fd5b2b88dc647f754bfbf7b6125cbe8 100644
--- a/tests/netdev_test/main.c
+++ b/tests/netdev_test/main.c
@@ -61,7 +61,7 @@ static uint8_t _tmp_len = 0;
 
 static void _dev_isr(netdev_t *dev);
 static int _dev_recv(netdev_t *dev, char *buf, int len, void *info);
-static int _dev_send(netdev_t *dev, const struct iovec *vector, int count);
+static int _dev_send(netdev_t *dev, const iolist_t *iolist);
 static int _dev_get_addr(netdev_t *dev, void *value, size_t max_len);
 static int _dev_set_addr(netdev_t *dev, const void *value, size_t max_len);
 
@@ -296,26 +296,26 @@ static int _dev_recv(netdev_t *dev, char *buf, int len, void *info)
     }
 }
 
-static int _dev_send(netdev_t *dev, const struct iovec *vector, int count)
+static int _dev_send(netdev_t *dev, const iolist_t *iolist)
 {
     int idx = 0;
 
     (void)dev;
     /* check packet content with expected data */
-    for (int i = 0; i < count; i++) {
-        if (memcmp(&(_tmp[idx]), vector[i].iov_base, vector[i].iov_len) != 0) {
-            printf("Unexpected send data (vector index = %d)\n", i);
+    for (; iolist; iolist = iolist->iol_next) {
+        if (memcmp(&(_tmp[idx]), iolist->iol_base, iolist->iol_len) != 0) {
+            puts("Unexpected send data:");
             puts("===========================================================");
             puts("expected");
             puts("===========================================================");
-            od_hex_dump(&_tmp[idx], vector[i].iov_len, OD_WIDTH_DEFAULT);
+            od_hex_dump(&_tmp[idx], iolist->iol_len, OD_WIDTH_DEFAULT);
             puts("===========================================================");
             puts("send data");
             puts("===========================================================");
-            od_hex_dump(vector[i].iov_base, vector[i].iov_len, OD_WIDTH_DEFAULT);
+            od_hex_dump(iolist->iol_base, iolist->iol_len, OD_WIDTH_DEFAULT);
             return -EINVAL;
         }
-        idx += vector[i].iov_len;
+        idx += iolist->iol_len;
     }
     if (idx != _tmp_len) {
         printf("Unexpected send length: %d (expected: %d)\n", idx, _tmp_len);
diff --git a/tests/socket_zep/main.c b/tests/socket_zep/main.c
index faac4e172ee8ba04a4c1feb5bd1be8f5f6298a16..d5e9ee382303635de505d14cd1cba4fb9abd7d25 100644
--- a/tests/socket_zep/main.c
+++ b/tests/socket_zep/main.c
@@ -56,29 +56,29 @@ static void test_init(void)
     _print_info(netdev);
 }
 
-static void test_send__vector_NULL__count_0(void)
+static void test_send__iolist_NULL(void)
 {
     netdev_t *netdev = (netdev_t *)(&_dev);
-    int res;
 
     puts("Send zero-length packet");
-    res = netdev->driver->send(netdev, NULL, 0);
+    int res = netdev->driver->send(netdev, NULL);
     assert((res < 0) || (res == 0));
     if ((res < 0) && (errno == ECONNREFUSED)) {
         puts("No remote socket exists (use scripts in `tests/` to have proper tests)");
     }
 }
 
-static void test_send__vector_not_NULL__count_2(void)
+static void test_send__iolist_not_NULL(void)
 {
-    struct iovec vector[] = { { .iov_base = "Hello", .iov_len = sizeof("Hello") },
-                              { .iov_base = "World", .iov_len = sizeof("World") } };
+    iolist_t iolist[] = { { .iol_base = "Hello", .iol_len = sizeof("Hello") },
+                          { .iol_base = "World", .iol_len = sizeof("World") } };
+
+    iolist[0].iol_next = &iolist[1];
+
     netdev_t *netdev = (netdev_t *)(&_dev);
-    int res;
 
     puts("Send 'Hello\\0World\\0'");
-    res =  netdev->driver->send(netdev, vector,
-                                sizeof(vector) / sizeof(struct iovec));
+    int res =  netdev->driver->send(netdev, iolist);
     assert((res < 0) || (res == (sizeof("Hello")) + sizeof("World")));
     if ((res < 0) && (errno == ECONNREFUSED)) {
         puts("No remote socket exists (use scripts in `tests/` to have proper tests)");
@@ -109,8 +109,8 @@ int main(void)
     _main_pid = sched_active_pid;
 
     test_init();
-    test_send__vector_NULL__count_0();
-    test_send__vector_not_NULL__count_2();
+    test_send__iolist_NULL();
+    test_send__iolist_not_NULL();
     test_recv();    /* does not return */
     puts("ALL TESTS SUCCESSFUL");
     return 0;
diff --git a/tests/unittests/tests-pkt/tests-pkt.c b/tests/unittests/tests-pkt/tests-pkt.c
index 2e48bd626eaa693661578d52e76ab56a931d13bb..ac8e123a8b8011641a8a90131fbc86da562a41ee 100644
--- a/tests/unittests/tests-pkt/tests-pkt.c
+++ b/tests/unittests/tests-pkt/tests-pkt.c
@@ -13,21 +13,27 @@
  * @file
  */
 #include <errno.h>
+#include <stddef.h>
 #include <stdint.h>
+#include <string.h>
 
 #include "embUnit/embUnit.h"
 #include "net/gnrc/pkt.h"
 #include "net/gnrc/nettype.h"
 
+#include "iolist.h"
+
 #include "unittests-constants.h"
 #include "tests-pkt.h"
 
-#define _INIT_ELEM(len, data, next) \
-    { 1, (next), (data), (len), GNRC_NETTYPE_UNDEF }
+#define _INIT_ELEM(len, _data, _next) \
+    { .users = 1, .next = (_next), .data = (_data), \
+      .size = (len), .type = GNRC_NETTYPE_UNDEF \
+    }
 #define _INIT_ELEM_STATIC_DATA(data, next) _INIT_ELEM(sizeof(data), data, next)
 
-#define _INIT_ELEM_STATIC_TYPE(type, next) \
-    { 1, (next), NULL, 0, (type) }
+#define _INIT_ELEM_STATIC_TYPE(_type, _next) \
+    { .users = 1, .next = (_next), .data = NULL, .size = 0, .type = (_type) }
 
 static void test_pkt_len__NULL(void)
 {
@@ -129,6 +135,40 @@ static void test_pktsnip_search_type(void)
     TEST_ASSERT_NULL(gnrc_pktsnip_search_type(&snip3, GNRC_NETTYPE_NUMOF));
 }
 
+static void test_pkt_equals_iolist(void)
+{
+    iolist_t iol;
+    gnrc_pktsnip_t pkt;
+
+    memset(&iol, '\0', sizeof(iol));
+    memset(&pkt, '\0', sizeof(pkt));
+
+    /* compare empty structs */
+    TEST_ASSERT_EQUAL_INT(0, memcmp(&iol, &pkt, sizeof(iol)));
+
+    /* check next pointer position */
+    iol.iol_next = (void *)0xAAAAAAAA;
+    pkt.next = (void *)0xAAAAAAAA;
+
+    TEST_ASSERT_EQUAL_INT(0, memcmp(&iol, &pkt, sizeof(iol)));
+
+    /* check data pointer position */
+    iol.iol_base = &iol;
+    pkt.data = &iol;
+
+    TEST_ASSERT_EQUAL_INT(0, memcmp(&iol, &pkt, sizeof(iol)));
+
+    /* check size position */
+    iol.iol_len = 0x12345678;
+    pkt.size = 0x12345678;
+
+    TEST_ASSERT_EQUAL_INT(0, memcmp(&iol, &pkt, sizeof(iol)));
+
+    TEST_ASSERT_EQUAL_INT(offsetof(iolist_t, iol_next), offsetof(gnrc_pktsnip_t, next));
+    TEST_ASSERT_EQUAL_INT(offsetof(iolist_t, iol_base), offsetof(gnrc_pktsnip_t, data));
+    TEST_ASSERT_EQUAL_INT(offsetof(iolist_t, iol_len), offsetof(gnrc_pktsnip_t, size));
+}
+
 Test *tests_pkt_tests(void)
 {
     EMB_UNIT_TESTFIXTURES(fixtures) {
@@ -143,6 +183,7 @@ Test *tests_pkt_tests(void)
         new_TestFixture(test_pkt_count__5_elem),
         new_TestFixture(test_pkt_count__null),
         new_TestFixture(test_pktsnip_search_type),
+        new_TestFixture(test_pkt_equals_iolist),
     };
 
     EMB_UNIT_TESTCALLER(pkt_tests, NULL, NULL, fixtures);
diff --git a/tests/unittests/tests-pktbuf/tests-pktbuf.c b/tests/unittests/tests-pktbuf/tests-pktbuf.c
index 959736022cfbe89e77227e4cb2cdea1b84a5cf3d..dbf8e8c98e41658aae6161a2af6ddc1225854bc9 100644
--- a/tests/unittests/tests-pktbuf/tests-pktbuf.c
+++ b/tests/unittests/tests-pktbuf/tests-pktbuf.c
@@ -321,7 +321,7 @@ static void test_pktbuf_mark__pkt_NOT_NULL__size_greater_than_pkt_size(void)
 
 static void test_pktbuf_mark__pkt_NOT_NULL__pkt_data_NULL(void)
 {
-    gnrc_pktsnip_t pkt = { 1, NULL, NULL, sizeof(TEST_STRING16), GNRC_NETTYPE_TEST };
+    gnrc_pktsnip_t pkt = { NULL, NULL, sizeof(TEST_STRING16), 1, GNRC_NETTYPE_TEST };
 
     TEST_ASSERT_NULL(gnrc_pktbuf_mark(&pkt, sizeof(TEST_STRING16) - 1,
                                       GNRC_NETTYPE_TEST));
@@ -661,7 +661,7 @@ static void test_pktbuf_hold__pkt_null(void)
 
 static void test_pktbuf_hold__pkt_external(void)
 {
-    gnrc_pktsnip_t pkt = { 1, NULL, TEST_STRING8, sizeof(TEST_STRING8), GNRC_NETTYPE_TEST };
+    gnrc_pktsnip_t pkt = { NULL, TEST_STRING8, sizeof(TEST_STRING8), 1, GNRC_NETTYPE_TEST };
 
     gnrc_pktbuf_hold(&pkt, 1);
     TEST_ASSERT(gnrc_pktbuf_is_empty());
diff --git a/tests/unittests/tests-pktqueue/tests-pktqueue.c b/tests/unittests/tests-pktqueue/tests-pktqueue.c
index 31ef4fa8e541c29daf7ff6067832f4383d95855c..6d10f6ee2ce617fda2a3ba40e4de0258ecb711bf 100644
--- a/tests/unittests/tests-pktqueue/tests-pktqueue.c
+++ b/tests/unittests/tests-pktqueue/tests-pktqueue.c
@@ -22,7 +22,7 @@
 #include "tests-pktqueue.h"
 
 #define PKT_INIT_ELEM(len, data, next) \
-    { 1, (next), (data), (len), GNRC_NETTYPE_UNDEF }
+    { (next), (data), (len), 1, GNRC_NETTYPE_UNDEF }
 #define PKT_INIT_ELEM_STATIC_DATA(data, next) PKT_INIT_ELEM(sizeof(data), data, next)
 #define PKTQUEUE_INIT_ELEM(pkt) { NULL, pkt }
 
diff --git a/tests/unittests/tests-priority_pktqueue/tests-priority_pktqueue.c b/tests/unittests/tests-priority_pktqueue/tests-priority_pktqueue.c
index 3673fa7f2a9f3559b337edcca4b904deed1afc89..3f90257857679f0dd80749194ed9ea5fddb0b6cb 100644
--- a/tests/unittests/tests-priority_pktqueue/tests-priority_pktqueue.c
+++ b/tests/unittests/tests-priority_pktqueue/tests-priority_pktqueue.c
@@ -23,7 +23,7 @@
 #include "tests-priority_pktqueue.h"
 
 #define PKT_INIT_ELEM(len, data, next) \
-    { 1, (next), (data), (len), GNRC_NETTYPE_UNDEF }
+    { (next), (data), (len), 1, GNRC_NETTYPE_UNDEF }
 #define PKT_INIT_ELEM_STATIC_DATA(data, next) PKT_INIT_ELEM(sizeof(data), data, next)
 #define PKTQUEUE_INIT_ELEM(pkt) { NULL, pkt }