This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 34d2cde8a8 net/l2/l3/l4: add support of iob offload
34d2cde8a8 is described below

commit 34d2cde8a8ffb4531a94754a9435a0ee4afc9a7b
Author: chao an <anc...@xiaomi.com>
AuthorDate: Wed Nov 23 19:39:40 2022 +0800

    net/l2/l3/l4: add support of iob offload
    
    1. Add new config CONFIG_NET_LL_GUARDSIZE to isolation of l2 stack,
       which will benefit l3(IP) layer for multi-MAC(l2) implementation,
       especially in some NICs such as celluler net driver.
    
    new configuration options: CONFIG_NET_LL_GUARDSIZE
    
    CONFIG_NET_LL_GUARDSIZE will reserved l2 buffer header size of
    network buffer to isolate the L2/L3 (MAC/IP) data on network layer,
    which will be beneficial to L3 network layer protocol transparent
    transmission and forwarding
    
    ------------------------------------------------------------
    Layout of frist iob entry:
    
            iob_data (aligned by CONFIG_IOB_ALIGNMENT)
                |
                |                  io_offset(CONFIG_NET_LL_GUARDSIZE)
                |                                |
                -------------------------------------------------
          iob   |            Reserved            |    io_len    |
                -------------------------------------------------
    
    -------------------------------------------------------------
    Layout of different NICs implementation:
    
            iob_data (aligned by CONFIG_IOB_ALIGNMENT)
                |
                |                 io_offset(CONFIG_NET_LL_GUARDSIZE)
                |                                |
                -------------------------------------------------
     Ethernet   |       Reserved    | ETH_HDRLEN |    io_len    |
                ---------------------------------|---------------
     8021Q      |   Reserved  | ETH_8021Q_HDRLEN |    io_len    |
                ---------------------------------|---------------
     ipforward  |            Reserved            |    io_len    |
                -------------------------------------------------
    
    --------------------------------------------------------------------
    
    2. Support iob offload to l2 driver to avoid unnecessary memory copy
    
    Support send/receive iob vectors directly between the NICs and l3/l4
    stack to avoid unnecessary memory copies, especially on hardware that
    supports Scatter/gather, which can greatly improve performance.
    
    new interface to support iob offload:
    
      ------------------------------------------
      |    IOB version     |     original      |
      |----------------------------------------|
      |  devif_iob_poll()  |   devif_poll()    |
      |       ...          |       ...         |
      ------------------------------------------
    
    --------------------------------------------------------------------
    
    1> NIC hardware support Scatter/gather transfer
    
    TX:
    
                    tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4)
                               /              \
                              /                \
    devif_poll_[l3|l4]_connections()     devif_iob_send() (nocopy:udp/icmp/...)
               /                                   \      (copy:tcp)
              /                                     \
      devif_iob_poll("NIC"_txpoll)                callback() // "NIC"_txpoll
                                                      |
                                dev->d_iob:           |
                                                    ---------------         
---------------
                                 io_data       iob1 |  |          |    iob3 |  
|          |
                                        \           ---------------         
---------------
                                      ---------------  |       --------------- |
                                 iob0 |  |          |  |  iob2 |  |          | |
                                      ---------------  |       --------------- |
                                         \             |          /           /
                                            \          |       /           /
                                       
----------------------------------------------
                        NICs io vector |    |    |    |    |    |    |    |    
|    |
                                       
----------------------------------------------
    
    RX:
    
      [tcp|udp|icmp|...]ipv[4|6]_data_handler()(iob_concat/append to readahead)
                        |
                        |
          [tcp|udp|icmp|...]_ipv[4|6]_in()/...
                        |
                        |
              pkt/ipv[4/6]_input()/...
                        |
                        |
         NICs io vector receive(iov_base to each iobs)
    
    --------------------------------------------------------------------
    
    2> CONFIG_IOB_BUFSIZE is greater than MTU:
    
    TX:
    
    "(CONFIG_IOB_BUFSIZE) > (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE + 
CONFIG_NET_LL_GUARDSIZE)"
    
                    tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4)
                               /              \
                              /                \
    devif_poll_[l3|l4]_connections()     devif_iob_send() (nocopy:udp/icmp/...)
               /                                   \      (copy:tcp)
              /                                     \
      devif_iob_poll("NIC"_txpoll)                callback() // "NIC"_txpoll
                                                      |
                                                 "NIC"_send()
                              (dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE - 
NET_LL_HDRLEN(dev)])
    
    RX:
    
      [tcp|udp|icmp|...]ipv[4|6]_data_handler()(iob_concat/append to readahead)
                        |
                        |
          [tcp|udp|icmp|...]_ipv[4|6]_in()/...
                        |
                        |
              pkt/ipv[4/6]_input()/...
                        |
                        |
         NICs io vector receive(iov_base to io_data)
    
    --------------------------------------------------------------------
    
    3> Compatible with all old flat buffer NICs
    
    TX:
                    tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4)
                               /              \
                              /                \
    devif_poll_[l3|l4]_connections()     devif_iob_send() (nocopy:udp/icmp/...)
               /                                   \      (copy:tcp)
              /                                     \
      devif_iob_poll(devif_poll_callback())  devif_poll_callback() /* new 
interface, gather iobs to flat buffer */
           /                                           \
          /                                             \
     devif_poll("NIC"_txpoll)                     "NIC"_send()(dev->d_buf)
    
    RX:
    
      [tcp|udp|icmp|...]ipv[4|6]_data_handler()(iob_concat/append to readahead)
                        |
                        |
          [tcp|udp|icmp|...]_ipv[4|6]_in()/...
                        |
                        |
                   netdev_input()  /* new interface, Scatter/gather flat/iob 
buffer */
                        |
                        |
              pkt/ipv[4|6]_input()/...
                        |
                        |
        NICs io vector receive(Orignal flat buffer)
    
    3. Iperf passthrough on NuttX simulator:
    
      -------------------------------------------------
      |  Protocol      | Server | Client |            |
      |-----------------------------------------------|
      |  TCP           |  813   |   834  |  Mbits/sec |
      |  TCP(Offload)  | 1720   |  1100  |  Mbits/sec |
      |  UDP           |   22   |   757  |  Mbits/sec |
      |  UDP(Offload)  |   25   |  1250  |  Mbits/sec |
      -------------------------------------------------
    
    Signed-off-by: chao an <anc...@xiaomi.com>
---
 arch/sim/src/sim/sim_netdriver.c     |  64 ++++++----
 drivers/net/loopback.c               |   2 -
 include/nuttx/mm/iob.h               |   2 +-
 include/nuttx/net/netdev.h           | 133 ++++++++++++++++++++-
 mm/iob/Kconfig                       |   2 +-
 net/Kconfig                          |   9 ++
 net/arp/arp_format.c                 |   4 +
 net/can/can.h                        |   8 +-
 net/can/can_callback.c               | 108 +++++++----------
 net/can/can_recvmsg.c                |  40 ++-----
 net/devif/Make.defs                  |  49 ++++----
 net/devif/devif.h                    |  40 ++++++-
 net/devif/devif_cansend.c            |  49 +++-----
 net/devif/devif_iobsend.c            |  73 ++++++++++--
 net/devif/devif_loopback.c           |  23 +++-
 net/devif/devif_pktsend.c            |  49 +++-----
 net/devif/devif_poll.c               | 222 +++++++++++++++++++++++++++++++++--
 net/devif/devif_send.c               |  21 +++-
 net/devif/ipv4_input.c               |  81 +++++++++++--
 net/devif/ipv6_input.c               |  80 +++++++++++--
 net/icmp/icmp_input.c                |  89 ++++----------
 net/icmp/icmp_recvmsg.c              |  54 +--------
 net/icmp/icmp_reply.c                |  65 +++++++++-
 net/icmp/icmp_sendmsg.c              |   8 +-
 net/icmpv6/icmpv6_advertise.c        |   4 +
 net/icmpv6/icmpv6_input.c            |  82 +++----------
 net/icmpv6/icmpv6_radvertise.c       |   4 +
 net/icmpv6/icmpv6_recvmsg.c          |  54 +--------
 net/icmpv6/icmpv6_reply.c            |  63 +++++++++-
 net/icmpv6/icmpv6_rsolicit.c         |   4 +
 net/icmpv6/icmpv6_sendmsg.c          |   6 +-
 net/icmpv6/icmpv6_solicit.c          |   4 +
 net/igmp/igmp_send.c                 |   4 +
 net/ipforward/ipv4_forward.c         |  42 +------
 net/ipforward/ipv6_forward.c         |  40 +------
 net/mld/mld_send.c                   |   4 +
 net/neighbor/neighbor_ethernet_out.c |   4 +
 net/netdev/Make.defs                 |   4 +
 net/netdev/netdev.h                  |   1 +
 net/netdev/netdev_input.c            | 117 ++++++++++++++++++
 net/netdev/netdev_iob.c              | 128 ++++++++++++++++++++
 net/pkt/pkt_input.c                  |  56 +++++++--
 net/pkt/pkt_recvmsg.c                |   6 +-
 net/tcp/tcp.h                        |  22 +++-
 net/tcp/tcp_callback.c               | 146 ++++++++---------------
 net/tcp/tcp_recvfrom.c               |  27 +++--
 net/tcp/tcp_send.c                   |  88 ++++++++++++--
 net/tcp/tcp_send_buffered.c          |   6 +-
 net/tcp/tcp_send_unbuffered.c        |   8 +-
 net/udp/udp.h                        |  19 ++-
 net/udp/udp_callback.c               |  99 ++++------------
 net/udp/udp_recvfrom.c               | 135 +++++++--------------
 net/udp/udp_send.c                   |  46 ++++++++
 net/udp/udp_sendto_buffered.c        |  29 ++++-
 net/udp/udp_sendto_unbuffered.c      |   7 +-
 net/utils/net_chksum.c               |  78 ++++++++++++
 net/utils/net_icmpchksum.c           |  16 +++
 net/utils/net_ipchksum.c             |  24 +++-
 net/utils/utils.h                    |  13 ++
 59 files changed, 1759 insertions(+), 906 deletions(-)

diff --git a/arch/sim/src/sim/sim_netdriver.c b/arch/sim/src/sim/sim_netdriver.c
index d98c6fa99b..999459a63d 100644
--- a/arch/sim/src/sim/sim_netdriver.c
+++ b/arch/sim/src/sim/sim_netdriver.c
@@ -72,6 +72,11 @@
 
 #include "sim_internal.h"
 
+#if CONFIG_IOB_BUFSIZE >= (MAX_NETDEV_PKTSIZE + \
+    CONFIG_NET_GUARDSIZE + CONFIG_NET_LL_GUARDSIZE)
+#  define SIM_NETDEV_IOB_OFFLOAD
+#endif
+
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
@@ -95,12 +100,23 @@ static struct net_driver_s 
g_sim_dev[CONFIG_SIM_NETDEV_NUMBER];
  * Private Functions
  ****************************************************************************/
 
-static void netdriver_reply(struct net_driver_s *dev)
+static void netdriver_send(struct net_driver_s *dev)
 {
   int devidx = (intptr_t)dev->d_private;
+#ifdef SIM_NETDEV_IOB_OFFLOAD
+  uint8_t *buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE -
+                                      NET_LL_HDRLEN(dev)];
+#else
+  uint8_t *buf = dev->d_buf;
+#endif
 
   UNUSED(devidx);
 
+  sim_netdev_send(devidx, buf, dev->d_len);
+}
+
+static void netdriver_reply(struct net_driver_s *dev)
+{
   /* If the receiving resulted in data that should be sent out on
    * the network, the field d_len is set to a value > 0.
    */
@@ -110,7 +126,7 @@ static void netdriver_reply(struct net_driver_s *dev)
       /* Send the packet */
 
       NETDEV_TXPACKETS(dev);
-      sim_netdev_send(devidx, dev->d_buf, dev->d_len);
+      netdriver_send(dev);
       NETDEV_TXDONE(dev);
     }
 }
@@ -131,6 +147,14 @@ static void netdriver_recv_work(void *arg)
 
   while (sim_netdev_avail(devidx))
     {
+#ifdef SIM_NETDEV_IOB_OFFLOAD
+      if (netdev_iob_prepare(dev, false, 0) != OK)
+        {
+          netdriver_txdone_interrupt(dev);
+          break;
+        }
+#endif
+
       /* sim_netdev_read will return 0 on a timeout event and > 0
        * on a data received event
        */
@@ -142,6 +166,10 @@ static void netdriver_recv_work(void *arg)
         {
           NETDEV_RXPACKETS(dev);
 
+#ifdef SIM_NETDEV_IOB_OFFLOAD
+          iob_update_pktlen(dev->d_iob, dev->d_len - NET_LL_HDRLEN(dev));
+#endif
+
           /* Data received event.  Check for valid Ethernet header with
            * destination == our MAC address
            */
@@ -208,7 +236,7 @@ static void netdriver_recv_work(void *arg)
 
                   if (dev->d_len > 0)
                     {
-                      sim_netdev_send(devidx, dev->d_buf, dev->d_len);
+                      netdriver_send(dev);
                     }
                 }
               else
@@ -224,6 +252,10 @@ static void netdriver_recv_work(void *arg)
               NETDEV_RXERRORS(dev);
             }
         }
+
+#ifdef SIM_NETDEV_IOB_OFFLOAD
+      netdev_iob_release(dev);
+#endif
     }
 
   net_unlock();
@@ -231,14 +263,10 @@ static void netdriver_recv_work(void *arg)
 
 static int netdriver_txpoll(struct net_driver_s *dev)
 {
-  int devidx = (intptr_t)dev->d_private;
-
-  UNUSED(devidx);
-
   /* Send the packet */
 
   NETDEV_TXPACKETS(dev);
-  sim_netdev_send(devidx, dev->d_buf, dev->d_len);
+  netdriver_send(dev);
   NETDEV_TXDONE(dev);
 
   /* If zero is returned, the polling will continue until all connections
@@ -321,9 +349,8 @@ static void netdriver_rxready_interrupt(void *priv)
 int sim_netdriver_init(void)
 {
   struct net_driver_s *dev;
-  void *pktbuf;
-  int pktsize;
   int devidx;
+
   for (devidx = 0; devidx < CONFIG_SIM_NETDEV_NUMBER; devidx++)
     {
       dev = &g_sim_dev[devidx];
@@ -334,22 +361,19 @@ int sim_netdriver_init(void)
                   netdriver_txdone_interrupt,
                   netdriver_rxready_interrupt);
 
-      /* Update the buffer size */
-
-      pktsize = dev->d_pktsize ? dev->d_pktsize :
-                (MAX_NETDEV_PKTSIZE + CONFIG_NET_GUARDSIZE);
-
       /* Allocate packet buffer */
 
-      pktbuf = kmm_malloc(pktsize);
-      if (pktbuf == NULL)
+#ifndef SIM_NETDEV_IOB_OFFLOAD
+      dev->d_buf = kmm_malloc(dev->d_pktsize != 0 ?
+                              dev->d_pktsize :
+                              (MAX_NETDEV_PKTSIZE +
+                               CONFIG_NET_GUARDSIZE));
+      if (dev->d_buf == NULL)
         {
           return -ENOMEM;
         }
+#endif
 
-      /* Set callbacks */
-
-      dev->d_buf     = pktbuf;
       dev->d_ifup    = netdriver_ifup;
       dev->d_ifdown  = netdriver_ifdown;
       dev->d_txavail = netdriver_txavail;
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index a90e203420..55e14aa7c6 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -82,7 +82,6 @@ struct lo_driver_s
  ****************************************************************************/
 
 static struct lo_driver_s g_loopback;
-static uint8_t g_iobuffer[NET_LO_PKTSIZE + CONFIG_NET_GUARDSIZE];
 
 /****************************************************************************
  * Private Function Prototypes
@@ -334,7 +333,6 @@ int localhost_initialize(void)
   priv->lo_dev.d_addmac  = lo_addmac;    /* Add multicast MAC address */
   priv->lo_dev.d_rmmac   = lo_rmmac;     /* Remove multicast MAC address */
 #endif
-  priv->lo_dev.d_buf     = g_iobuffer;   /* Attach the IO buffer */
   priv->lo_dev.d_private = priv;         /* Used to recover private state from 
dev */
 
   /* Register the loopabck device with the OS so that socket IOCTLs can b
diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h
index 0231a1cbbc..fa672d0cd5 100644
--- a/include/nuttx/mm/iob.h
+++ b/include/nuttx/mm/iob.h
@@ -127,7 +127,7 @@ struct iob_s
 #if CONFIG_IOB_HEADSIZE > 0
   uint8_t  io_head[CONFIG_IOB_HEADSIZE];
 #endif
-  uint8_t  io_data[CONFIG_IOB_BUFSIZE];
+  uint8_t  io_data[CONFIG_IOB_BUFSIZE] aligned_data(CONFIG_IOB_ALIGNMENT);
 };
 
 #if CONFIG_IOB_NCHAINS > 0
diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h
index 435480b99b..ebdceb6d4c 100644
--- a/include/nuttx/net/netdev.h
+++ b/include/nuttx/net/netdev.h
@@ -59,8 +59,11 @@
 #include <net/ethernet.h>
 #include <arpa/inet.h>
 
+#include <nuttx/mm/iob.h>
 #include <nuttx/net/netconfig.h>
+#include <nuttx/net/net.h>
 #include <nuttx/net/ip.h>
+#include <nuttx/nuttx.h>
 
 #ifdef CONFIG_NET_IGMP
 #  include <nuttx/net/igmp.h>
@@ -307,6 +310,20 @@ struct net_driver_s
   net_ipv6addr_t d_ipv6draddr;  /* Default router IPv6 address */
   net_ipv6addr_t d_ipv6netmask; /* Network IPv6 subnet mask */
 #endif
+  /* This is a new design that uses d_iob as packets input and output
+   * buffer which used by some NICs such as celluler net driver. Case for
+   * data input, note that d_iob maybe a linked chain only when using
+   * d_iob to store reassembled datagrams, otherwise d_iob uses only
+   * one buffer to hold the entire frame data. Case for data output, note
+   * that d_iob also maybe a linked chain only when using d_iob to
+   * store fragmented datagrams, otherwise d_iob uses only one buffer
+   * to hold the entire frame data.
+   *
+   * In all cases mentioned above, d_buf always points to the beginning
+   * of the first buffer of d_iob.
+   */
+
+  FAR struct iob_s *d_iob;
 
   /* The d_buf array is used to hold incoming and outgoing packets. The
    * device driver should place incoming data into this buffer.  When sending
@@ -427,10 +444,6 @@ struct net_driver_s
 
 typedef CODE int (*devif_poll_callback_t)(FAR struct net_driver_s *dev);
 
-/****************************************************************************
- * Public Data
- ****************************************************************************/
-
 /****************************************************************************
  * Public Function Prototypes
  ****************************************************************************/
@@ -560,6 +573,19 @@ int sixlowpan_input(FAR struct radio_driver_s *ieee,
  *     return 0;
  *   }
  *
+ *   Compatible with all old flat buffer NICs
+ *
+ *                 tcp_poll()/udp_poll()/pkt_poll()/...(l3/l4)
+ *                            /              \
+ *                           /                \
+ * devif_poll_[l3/l4]_connections()     devif_iob_send() (nocopy:udp/icmp/..)
+ *            /                                   \      (copy:tcp)
+ *           /                                     \
+ *   devif_iob_poll(devif_poll_callback())  devif_poll_callback()
+ *        /                                           \
+ *       /                                             \
+ *  devif_poll("NIC"_txpoll)                     "NIC"_send()(dev->d_buf)
+ *
  ****************************************************************************/
 
 int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback);
@@ -640,6 +666,26 @@ int netdev_carrier_off(FAR struct net_driver_s *dev);
 
 uint16_t chksum(uint16_t sum, FAR const uint8_t *data, uint16_t len);
 
+/****************************************************************************
+ * Name: chksum_iob
+ *
+ * Description:
+ *   Calculate the Internet checksum over an iob chain buffer.
+ *
+ * Input Parameters:
+ *   sum    - Partial calculations carried over from a previous call to
+ *            chksum().  This should be zero on the first time that check
+ *            sum is called.
+ *   iob    - An iob chain buffer over which the checksum is to be computed.
+ *   offset - Specifies the byte offset of the start of valid data.
+ *
+ * Returned Value:
+ *   The updated checksum value.
+ *
+ ****************************************************************************/
+
+uint16_t chksum_iob(uint16_t sum, FAR struct iob_s *iob, uint16_t offset);
+
 /****************************************************************************
  * Name: net_chksum
  *
@@ -668,6 +714,35 @@ uint16_t chksum(uint16_t sum, FAR const uint8_t *data, 
uint16_t len);
 
 uint16_t net_chksum(FAR uint16_t *data, uint16_t len);
 
+/****************************************************************************
+ * Name: net_chksum_iob
+ *
+ * Description:
+ *   Calculate the Internet checksum over an iob chain buffer.
+ *
+ *   The Internet checksum is the one's complement of the one's complement
+ *   sum of all 16-bit words in the buffer.
+ *
+ *   See RFC1071.
+ *
+ *   If CONFIG_NET_ARCH_CHKSUM is defined, then this function must be
+ *   provided by architecture-specific logic.
+ *
+ * Input Parameters:
+ *   sum    - Partial calculations carried over from a previous call to
+ *            chksum().  This should be zero on the first time that check
+ *            sum is called.
+ *   iob    - An iob chain buffer over which the checksum is to be computed.
+ *   offset - Specifies the byte offset of the start of valid data.
+ *
+ * Returned Value:
+ *   The Internet checksum of the given iob chain buffer.
+ *
+ ****************************************************************************/
+
+uint16_t net_chksum_iob(uint16_t sum, FAR struct iob_s *iob,
+                        uint16_t offset);
+
 /****************************************************************************
  * Name: ipv4_upperlayer_chksum
  *
@@ -802,4 +877,54 @@ void net_incr32(FAR uint8_t *op32, uint16_t op16);
 
 int netdev_lladdrsize(FAR struct net_driver_s *dev);
 
+/****************************************************************************
+ * Name: netdev_iob_prepare
+ *
+ * Description:
+ *   Prepare data buffer for a given NIC
+ *   The iob offset will be updated to l2 gruard size by default:
+ *  ----------------------------------------------------------------
+ *  |                     iob entry                                |
+ *  ---------------------------------------------------------------|
+ *  |<--- CONFIG_NET_LL_GUARDSIZE -->|<--- io_len/io_pktlen(0) --->|
+ *  ---------------------------------------------------------------|
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ * Returned Value:
+ *   A non-zero copy is returned on success.
+ *
+ ****************************************************************************/
+
+int netdev_iob_prepare(FAR struct net_driver_s *dev, bool throttled,
+                       unsigned int timeout);
+
+/****************************************************************************
+ * Name: netdev_iob_clear
+ *
+ * Description:
+ *   Clean up buffer resources for a given NIC
+ *
+ * Assumptions:
+ *   The caller has locked the network and dev->d_iob has been
+ *   released or taken away.
+ *
+ ****************************************************************************/
+
+void netdev_iob_clear(FAR struct net_driver_s *dev);
+
+/****************************************************************************
+ * Name: netdev_iob_release
+ *
+ * Description:
+ *   Release buffer resources for a given NIC
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+void netdev_iob_release(FAR struct net_driver_s *dev);
+
 #endif /* __INCLUDE_NUTTX_NET_NETDEV_H */
diff --git a/mm/iob/Kconfig b/mm/iob/Kconfig
index 31b8b1091e..6a1d07de70 100644
--- a/mm/iob/Kconfig
+++ b/mm/iob/Kconfig
@@ -41,7 +41,7 @@ config IOB_HEADSIZE
 
 config IOB_ALIGNMENT
        int "Alignment size of each I/O buffer"
-       default 1
+       default 4
        ---help---
                The member io_head of all I/O buffers is aligned to the value
                specified by this configuration.
diff --git a/net/Kconfig b/net/Kconfig
index c38539fcbf..5a8f0c0e2d 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -117,6 +117,15 @@ config NET_GUARDSIZE
                packet size will be chopped down to the size indicated in the 
TCP
                header.
 
+config NET_LL_GUARDSIZE
+       int "Data Link Layer(L2) Guard size of Network buffer(IOB)"
+       default 14 if NET_ETHERNET
+       default 0
+       ---help---
+               This is reserved l2 buffer header size of network buffer to 
isolate
+               the L2/L3 (MAC/IP) data on Network layer, which will be 
beneficial
+               to L3 network layer protocol transparent transmission and 
forwarding
+
 config NET_RECV_BUFSIZE
        int "Net Receive buffer size"
        default 0
diff --git a/net/arp/arp_format.c b/net/arp/arp_format.c
index 458a013ead..50782eaae4 100644
--- a/net/arp/arp_format.c
+++ b/net/arp/arp_format.c
@@ -97,6 +97,10 @@ void arp_format(FAR struct net_driver_s *dev, in_addr_t 
ipaddr)
 
   eth->type        = HTONS(ETHTYPE_ARP);
   dev->d_len       = sizeof(struct arp_hdr_s) + ETH_HDRLEN;
+
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, sizeof(struct arp_hdr_s));
 }
 
 #endif /* CONFIG_NET_ARP */
diff --git a/net/can/can.h b/net/can/can.h
index b1180c11aa..62a47cf59a 100644
--- a/net/can/can.h
+++ b/net/can/can.h
@@ -209,10 +209,8 @@ uint16_t can_callback(FAR struct net_driver_s *dev,
  *   receive the data.
  *
  * Input Parameters:
+ *   dev  - The device which as active when the event was detected.
  *   conn - A pointer to the CAN connection structure
- *   buffer - A pointer to the buffer to be copied to the read-ahead
- *     buffers
- *   buflen - The number of bytes to copy to the read-ahead buffer.
  *
  * Returned Value:
  *   The number of bytes actually buffered is returned.  This will be either
@@ -225,8 +223,8 @@ uint16_t can_callback(FAR struct net_driver_s *dev,
  *
  ****************************************************************************/
 
-uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer,
-                         uint16_t buflen);
+uint16_t can_datahandler(FAR struct net_driver_s *dev,
+                         FAR struct can_conn_s *conn);
 
 /****************************************************************************
  * Name: can_recvmsg
diff --git a/net/can/can_callback.c b/net/can/can_callback.c
index a5f3e17937..b080e4da6f 100644
--- a/net/can/can_callback.c
+++ b/net/can/can_callback.c
@@ -61,10 +61,9 @@ static inline uint16_t
 can_data_event(FAR struct net_driver_s *dev, FAR struct can_conn_s *conn,
                uint16_t flags)
 {
-  uint16_t ret;
-  FAR uint8_t *buffer = dev->d_appdata;
   int buflen = dev->d_len;
   uint16_t recvlen;
+  uint16_t ret;
 
   ret = (flags & ~CAN_NEWDATA);
 
@@ -72,7 +71,7 @@ can_data_event(FAR struct net_driver_s *dev, FAR struct 
can_conn_s *conn,
    * partial packets will not be buffered.
    */
 
-  recvlen = can_datahandler(conn, buffer, buflen);
+  recvlen = can_datahandler(dev, conn);
   if (recvlen < buflen)
     {
       /* There is no handler to receive new data and there are no free
@@ -120,21 +119,6 @@ uint16_t can_callback(FAR struct net_driver_s *dev,
 
   if (conn)
     {
-#ifdef CONFIG_NET_TIMESTAMP
-      /* TIMESTAMP sockopt is activated, create timestamp and copy to iob */
-
-      if (conn->timestamp)
-        {
-          struct timespec *ts = (struct timespec *)
-                                                &dev->d_appdata[dev->d_len];
-          struct timeval *tv = (struct timeval *)
-                                                &dev->d_appdata[dev->d_len];
-          dev->d_len += sizeof(struct timeval);
-          clock_systime_timespec(ts);
-          tv->tv_usec = ts->tv_nsec / 1000;
-        }
-#endif
-
       /* Try to lock the network when successful send data to the listener */
 
       if (net_trylock() == OK)
@@ -149,6 +133,34 @@ uint16_t can_callback(FAR struct net_driver_s *dev,
 
       if ((flags & CAN_NEWDATA) != 0)
         {
+#ifdef CONFIG_NET_TIMESTAMP
+          /* TIMESTAMP sockopt is activated,
+           * create timestamp and copy to iob
+           */
+
+          if (conn->timestamp)
+            {
+              struct timeval tv;
+              FAR struct timespec *ts = (FAR struct timespec *)&tv;
+              int len;
+
+              clock_systime_timespec(ts);
+              tv.tv_usec = ts->tv_nsec / 1000;
+
+              len = iob_trycopyin(dev->d_iob, (FAR uint8_t *)&tv,
+                                  sizeof(struct timeval), 0, false);
+              if (len != sizeof(struct timeval))
+                {
+                  dev->d_len = 0;
+                  return flags & ~CAN_NEWDATA;
+                }
+              else
+                {
+                  dev->d_len += len;
+                }
+            }
+
+#endif
           /* Data was not handled.. dispose of it appropriately */
 
           flags = can_data_event(dev, conn, flags);
@@ -168,10 +180,8 @@ uint16_t can_callback(FAR struct net_driver_s *dev,
  *   receive the data.
  *
  * Input Parameters:
+ *   dev  - The device which as active when the event was detected.
  *   conn - A pointer to the CAN connection structure
- *   buffer - A pointer to the buffer to be copied to the read-ahead
- *     buffers
- *   buflen - The number of bytes to copy to the read-ahead buffer.
  *
  * Returned Value:
  *   The number of bytes actually buffered is returned.  This will be either
@@ -184,58 +194,28 @@ uint16_t can_callback(FAR struct net_driver_s *dev,
  *
  ****************************************************************************/
 
-uint16_t can_datahandler(FAR struct can_conn_s *conn, FAR uint8_t *buffer,
-                         uint16_t buflen)
+uint16_t can_datahandler(FAR struct net_driver_s *dev,
+                         FAR struct can_conn_s *conn)
 {
-  FAR struct iob_s *iob;
+  FAR struct iob_s *iob = dev->d_iob;
   int ret;
 
-  /* Try to allocate on I/O buffer to start the chain without waiting (and
-   * throttling as necessary).  If we would have to wait, then drop the
-   * packet.
-   */
-
-  iob = iob_tryalloc(true);
-  if (iob == NULL)
-    {
-      nerr("ERROR: Failed to create new I/O buffer chain\n");
-      return 0;
-    }
-
-  /* Copy the new appdata into the I/O buffer chain (without waiting) */
-
-  ret = iob_trycopyin(iob, buffer, buflen, 0, true);
-  if (ret < 0)
-    {
-      /* On a failure, iob_copyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
-      iob_free_chain(iob);
-      return 0;
-    }
-
-  /* Add the new I/O buffer chain to the tail of the read-ahead queue (again
-   * without waiting).
-   */
+  /* Concat the iob to readahead */
 
   ret = iob_tryadd_queue(iob, &conn->readahead);
-  if (ret < 0)
+  if (ret >= 0)
     {
-      nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
-      iob_free_chain(iob);
-      return 0;
-    }
-
 #ifdef CONFIG_NET_CAN_NOTIFIER
-  /* Provide notification(s) that additional CAN read-ahead data is
-   * available.
-   */
+      /* Provide notification(s) that additional CAN read-ahead data is
+       * available.
+       */
 
-  can_readahead_signal(conn);
+      can_readahead_signal(conn);
 #endif
-  return buflen;
+      ret = iob->io_pktlen;
+    }
+
+  return ret;
 }
 
 #endif /* CONFIG_NET && CONFIG_NET_CAN */
diff --git a/net/can/can_recvmsg.c b/net/can/can_recvmsg.c
index 5ad1bfbb48..304535e885 100644
--- a/net/can/can_recvmsg.c
+++ b/net/can/can_recvmsg.c
@@ -120,8 +120,9 @@ static inline void can_add_recvlen(FAR struct 
can_recvfrom_s *pstate,
  ****************************************************************************/
 
 static size_t can_recvfrom_newdata(FAR struct net_driver_s *dev,
-                                 FAR struct can_recvfrom_s *pstate)
+                                   FAR struct can_recvfrom_s *pstate)
 {
+  unsigned int offset;
   size_t recvlen;
 
   if (dev->d_len > pstate->pr_buflen)
@@ -135,7 +136,13 @@ static size_t can_recvfrom_newdata(FAR struct net_driver_s 
*dev,
 
   /* Copy the new packet data into the user buffer */
 
-  memcpy(pstate->pr_buffer, dev->d_buf, recvlen);
+  offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset;
+
+  recvlen = iob_copyout(pstate->pr_buffer, dev->d_iob, recvlen, offset);
+
+  /* Trim the copied buffers */
+
+  dev->d_iob = iob_trimhead(dev->d_iob, recvlen + offset);
 
   /* Update the accumulated size of the data read */
 
@@ -175,34 +182,7 @@ static inline void can_newdata(FAR struct net_driver_s 
*dev,
 
   if (recvlen < dev->d_len)
     {
-      FAR struct can_conn_s *conn = pstate->pr_conn;
-      FAR uint8_t *buffer = dev->d_appdata + recvlen;
-      uint16_t buflen = dev->d_len - recvlen;
-#ifdef CONFIG_DEBUG_NET
-      uint16_t nsaved;
-
-      nsaved = can_datahandler(conn, buffer, buflen);
-#else
-      can_datahandler(conn, buffer, buflen);
-#endif
-
-      /* There are complicated buffering issues that are not addressed fully
-       * here.  For example, what if up_datahandler() cannot buffer the
-       * remainder of the packet?  In that case, the data will be dropped but
-       * still ACKed.  Therefore it would not be resent.
-       *
-       * This is probably not an issue here because we only get here if the
-       * read-ahead buffers are empty and there would have to be something
-       * serioulsy wrong with the configuration not to be able to buffer a
-       * partial packet in this context.
-       */
-
-#ifdef CONFIG_DEBUG_NET
-      if (nsaved < buflen)
-        {
-          nerr("ERROR: packet data not saved (%d bytes)\n", buflen - nsaved);
-        }
-#endif
+      can_datahandler(dev, pstate->pr_conn);
     }
 
   /* Indicate no data in the buffer */
diff --git a/net/devif/Make.defs b/net/devif/Make.defs
index 3fcfbf6135..89460d80c5 100644
--- a/net/devif/Make.defs
+++ b/net/devif/Make.defs
@@ -20,39 +20,44 @@
 
 # Network device interface source files
 
-NET_CSRCS += devif_initialize.c devif_send.c devif_poll.c devif_callback.c
-NET_CSRCS += devif_loopback.c
+NET_CSRCS += devif_initialize.c devif_callback.c
 
 # Device driver IP packet receipt interfaces
 
-ifeq ($(CONFIG_NET_IPv4),y)
-NET_CSRCS += ipv4_input.c
-endif
+ifeq ($(CONFIG_MM_IOB),y)
 
-ifeq ($(CONFIG_NET_IPv6),y)
-NET_CSRCS += ipv6_input.c
-endif
+  # IP packet
 
-# IP forwarding
+  NET_CSRCS += devif_send.c devif_loopback.c
 
-ifeq ($(CONFIG_NET_IPFORWARD),y)
-NET_CSRCS += devif_forward.c
-endif
+  ifeq ($(CONFIG_NET_IPv4),y)
+    NET_CSRCS += ipv4_input.c
+  endif
 
-# I/O buffer chain support required?
+  ifeq ($(CONFIG_NET_IPv6),y)
+    NET_CSRCS += ipv6_input.c
+  endif
 
-ifeq ($(CONFIG_MM_IOB),y)
-NET_CSRCS += devif_iobsend.c
-endif
+  # IP forwarding
 
-# Raw packet socket support
+  ifeq ($(CONFIG_NET_IPFORWARD),y)
+    NET_CSRCS += devif_forward.c
+  endif
 
-ifeq ($(CONFIG_NET_PKT),y)
-NET_CSRCS += devif_pktsend.c
-endif
+  # I/O buffer chain support required?
+
+  NET_CSRCS += devif_poll.c
+  NET_CSRCS += devif_iobsend.c
+
+  # Raw packet socket support
+
+  ifeq ($(CONFIG_NET_PKT),y)
+    NET_CSRCS += devif_pktsend.c
+  endif
 
-ifeq ($(CONFIG_NET_CAN),y)
-NET_CSRCS += devif_cansend.c
+  ifeq ($(CONFIG_NET_CAN),y)
+    NET_CSRCS += devif_cansend.c
+  endif
 endif
 
 # Include network device interface build support
diff --git a/net/devif/devif.h b/net/devif/devif.h
index 6b926813b7..378585f071 100644
--- a/net/devif/devif.h
+++ b/net/devif/devif.h
@@ -449,7 +449,8 @@ uint16_t devif_dev_event(FAR struct net_driver_s *dev, 
uint16_t flags);
  *
  ****************************************************************************/
 
-void devif_send(FAR struct net_driver_s *dev, FAR const void *buf, int len);
+void devif_send(FAR struct net_driver_s *dev, FAR const void *buf,
+                int len, unsigned int offset);
 
 /****************************************************************************
  * Name: devif_iob_send
@@ -469,7 +470,8 @@ void devif_send(FAR struct net_driver_s *dev, FAR const 
void *buf, int len);
 #ifdef CONFIG_MM_IOB
 struct iob_s;
 void devif_iob_send(FAR struct net_driver_s *dev, FAR struct iob_s *buf,
-                    unsigned int len, unsigned int offset);
+                    unsigned int len, unsigned int offset,
+                    unsigned int target_offset);
 #endif
 
 /****************************************************************************
@@ -560,6 +562,40 @@ int devif_poll_out(FAR struct net_driver_s *dev,
 
 int devif_loopback(FAR struct net_driver_s *dev);
 
+/****************************************************************************
+ * Name: netdev_input
+ *
+ * Description:
+ *   This function will copy the flat buffer that does not support
+ *   Scatter/gather to the iob vector buffer.
+ *
+ *   Compatible with all old flat buffer NICs:
+ *
+ *   [tcp|udp|icmp|...]ipv[4|6]_data_handler()
+ *                     |                    (iob_concat/append to readahead)
+ *                     |
+ *              pkt/ipv[4/6]_in()/...
+ *                     |
+ *                     |
+ *                netdev_input()  // new interface, Scatter/gather flat/iobs
+ *                     |
+ *                     |
+ *           pkt/ipv[4|6]_input()/...
+ *                     |
+ *                     |
+ *     NICs io vector receive(Orignal flat buffer)
+ *
+ * Input Parameters:
+ *   callback - Input callback of L3 stack
+ *
+ * Returned Value:
+ *   A non-zero copy is returned on success.
+ *
+ ****************************************************************************/
+
+int netdev_input(FAR struct net_driver_s *dev,
+                 devif_poll_callback_t callback, bool reply);
+
 #undef EXTERN
 #ifdef __cplusplus
 }
diff --git a/net/devif/devif_cansend.c b/net/devif/devif_cansend.c
index b82956c851..753868fe82 100644
--- a/net/devif/devif_cansend.c
+++ b/net/devif/devif_cansend.c
@@ -32,34 +32,6 @@
 
 #if defined(CONFIG_NET_CAN)
 
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/****************************************************************************
- * Private Type Declarations
- ****************************************************************************/
-
-/****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
-
-/****************************************************************************
- * Public Constant Data
- ****************************************************************************/
-
-/****************************************************************************
- * Public Data
- ****************************************************************************/
-
-/****************************************************************************
- * Private Constant Data
- ****************************************************************************/
-
-/****************************************************************************
- * Private Data
- ****************************************************************************/
-
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -83,16 +55,25 @@
 void devif_can_send(FAR struct net_driver_s *dev, FAR const void *buf,
                     unsigned int len)
 {
-  DEBUGASSERT(dev && len > 0 && len <= NETDEV_PKTSIZE(dev));
+  unsigned int limit = NETDEV_PKTSIZE(dev) -
+                       CONFIG_NET_LL_GUARDSIZE;
 
-  /* Copy the data into the device packet buffer */
+  if (dev == NULL || len == 0 || len > limit)
+    {
+      nerr("ERROR: devif_pkt_send fail: %p, sndlen: %u, pktlen: %u\n",
+           dev, len, limit);
+      return;
+    }
 
-  memcpy(dev->d_buf, buf, len);
+  iob_update_pktlen(dev->d_iob, 0);
 
-  /* Set the number of bytes to send */
+  /* Copy the data into the device packet buffer and set the number of
+   * bytes to send
+   */
 
-  dev->d_len    = len;
-  dev->d_sndlen = len;
+  dev->d_sndlen = iob_copyin(dev->d_iob, buf, len, 0, false) == len ?
+                  len : 0;
+  dev->d_len    = dev->d_sndlen;
 }
 
 #endif /* CONFIG_NET_CAN */
diff --git a/net/devif/devif_iobsend.c b/net/devif/devif_iobsend.c
index 496bb1df8f..96bf272749 100644
--- a/net/devif/devif_iobsend.c
+++ b/net/devif/devif_iobsend.c
@@ -53,19 +53,78 @@
  ****************************************************************************/
 
 void devif_iob_send(FAR struct net_driver_s *dev, FAR struct iob_s *iob,
-                    unsigned int len, unsigned int offset)
+                    unsigned int len, unsigned int offset,
+                    unsigned int target_offset)
 {
-  if (dev == NULL || len == 0 || len >= NETDEV_PKTSIZE(dev))
+  unsigned int limit = NETDEV_PKTSIZE(dev) -
+                       NET_LL_HDRLEN(dev) - target_offset;
+  unsigned int copyin;
+  int ret;
+
+  if (dev == NULL || len == 0 || len > limit)
     {
-      nerr("devif_iob_send error, %p, send len: %u, pkt len: %u\n",
-                                          dev, len, NETDEV_PKTSIZE(dev));
+      if (dev->d_iob == NULL)
+        {
+          iob_free_chain(iob);
+        }
+
+      nerr("devif_iob_send error, %p, send len: %u, limit len: %u\n",
+           dev, len, limit);
       return;
     }
 
-  /* Copy the data from the I/O buffer chain to the device buffer */
+  /* Append the send buffer after device buffer */
+
+  if (dev->d_iob != NULL)
+    {
+      /* Skip the l3/l4 offset before append */
+
+      iob_update_pktlen(dev->d_iob, target_offset);
+
+      /* Skip to the I/O buffer containing the data offset */
+
+      while (iob != NULL && offset > iob->io_len)
+        {
+          offset -= iob->io_len;
+          iob     = iob->io_flink;
+        }
+
+      dev->d_sndlen = len;
 
-  iob_copyout(dev->d_appdata, iob, len, offset);
-  dev->d_sndlen = len;
+      /* Clone the iob to target device buffer */
+
+      while (iob != NULL && len > 0)
+        {
+          copyin = (len > iob->io_len - offset) ?
+                   iob->io_len - offset : len;
+
+          ret = iob_copyin(dev->d_iob, iob->io_data +
+                                       iob->io_offset + offset,
+                           copyin, target_offset, false);
+          if (ret != copyin)
+            {
+              netdev_iob_release(dev);
+              dev->d_sndlen = 0;
+              nerr("devif_iob_send error, not enough iob entries, "
+                   "send len: %u\n", len);
+              return;
+            }
+
+          target_offset  += copyin;
+          len            -= copyin;
+          offset          = 0;
+          iob             = iob->io_flink;
+        }
+    }
+  else
+    {
+      /* Send the iob directly if no device buffer */
+
+      dev->d_iob    = iob;
+      dev->d_sndlen = len;
+      dev->d_buf    = &iob->io_data[CONFIG_NET_LL_GUARDSIZE -
+                                    NET_LL_HDRLEN(dev)];
+    }
 
 #ifdef CONFIG_NET_TCP_WRBUFFER_DUMP
   /* Dump the outgoing device buffer */
diff --git a/net/devif/devif_loopback.c b/net/devif/devif_loopback.c
index a2e6be82d5..7df7a20731 100644
--- a/net/devif/devif_loopback.c
+++ b/net/devif/devif_loopback.c
@@ -31,6 +31,17 @@
 #include <nuttx/net/pkt.h>
 #include <nuttx/net/netdev.h>
 
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* This is a helper pointer for accessing the contents of the ip header */
+
+#define LOIPv4BUF ((FAR struct ipv4_hdr_s *) \
+                   &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE])
+#define LOIPv6BUF ((FAR struct ipv6_hdr_s *) \
+                   &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE])
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -40,16 +51,16 @@ static bool is_loopback(FAR struct net_driver_s *dev)
   if (dev->d_len > 0)
     {
 #ifdef CONFIG_NET_IPv4
-      if ((IPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION &&
-           net_ipv4addr_hdrcmp(IPv4BUF->destipaddr, &dev->d_ipaddr))
+      if ((LOIPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION &&
+           net_ipv4addr_hdrcmp(LOIPv4BUF->destipaddr, &dev->d_ipaddr))
         {
           return true;
         }
 #endif
 
 #ifdef CONFIG_NET_IPv6
-      if ((IPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION &&
-          net_ipv6addr_hdrcmp(IPv6BUF->destipaddr, dev->d_ipv6addr))
+      if ((LOIPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION &&
+          net_ipv6addr_hdrcmp(LOIPv6BUF->destipaddr, dev->d_ipv6addr))
         {
           return true;
         }
@@ -99,7 +110,7 @@ int devif_loopback(FAR struct net_driver_s *dev)
       /* We only accept IP packets of the configured type */
 
 #ifdef CONFIG_NET_IPv4
-      if ((IPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION)
+      if ((LOIPv4BUF->vhl & IP_VERSION_MASK) == IPv4_VERSION)
         {
           ninfo("IPv4 frame\n");
 
@@ -109,7 +120,7 @@ int devif_loopback(FAR struct net_driver_s *dev)
       else
 #endif
 #ifdef CONFIG_NET_IPv6
-      if ((IPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION)
+      if ((LOIPv6BUF->vtc & IP_VERSION_MASK) == IPv6_VERSION)
         {
           ninfo("IPv6 frame\n");
 
diff --git a/net/devif/devif_pktsend.c b/net/devif/devif_pktsend.c
index 81df8e2591..8c98699e69 100644
--- a/net/devif/devif_pktsend.c
+++ b/net/devif/devif_pktsend.c
@@ -32,34 +32,6 @@
 
 #ifdef CONFIG_NET_PKT
 
-/****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-/****************************************************************************
- * Private Type Declarations
- ****************************************************************************/
-
-/****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
-
-/****************************************************************************
- * Public Constant Data
- ****************************************************************************/
-
-/****************************************************************************
- * Public Data
- ****************************************************************************/
-
-/****************************************************************************
- * Private Constant Data
- ****************************************************************************/
-
-/****************************************************************************
- * Private Data
- ****************************************************************************/
-
 /****************************************************************************
  * Public Functions
  ****************************************************************************/
@@ -83,16 +55,25 @@
 void devif_pkt_send(FAR struct net_driver_s *dev, FAR const void *buf,
                     unsigned int len)
 {
-  DEBUGASSERT(dev && len > 0 && len < NETDEV_PKTSIZE(dev));
+  unsigned int limit = NETDEV_PKTSIZE(dev) -
+                       CONFIG_NET_LL_GUARDSIZE;
 
-  /* Copy the data into the device packet buffer */
+  if (dev == NULL || len == 0 || len > limit)
+    {
+      nerr("ERROR: devif_pkt_send fail: %p, sndlen: %u, pktlen: %u\n",
+           dev, len, limit);
+      return;
+    }
 
-  memcpy(dev->d_buf, buf, len);
+  iob_update_pktlen(dev->d_iob, 0);
 
-  /* Set the number of bytes to send */
+  /* Copy the data into the device packet buffer and set the number of
+   * bytes to send
+   */
 
-  dev->d_len    = len;
-  dev->d_sndlen = len;
+  dev->d_sndlen = iob_copyin(dev->d_iob, buf, len, 0, false) == len ?
+                  len : 0;
+  dev->d_len    = dev->d_sndlen;
 }
 
 #endif /* CONFIG_NET_PKT */
diff --git a/net/devif/devif_poll.c b/net/devif/devif_poll.c
index 17daae4cf0..fef4e48bcf 100644
--- a/net/devif/devif_poll.c
+++ b/net/devif/devif_poll.c
@@ -618,11 +618,7 @@ static inline int devif_poll_tcp_connections(FAR struct 
net_driver_s *dev,
 #endif
 
 /****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-/****************************************************************************
- * Name: devif_poll
+ * Name: devif_poll_connections
  *
  * Description:
  *   This function will traverse each active network connection structure and
@@ -646,9 +642,10 @@ static inline int devif_poll_tcp_connections(FAR struct 
net_driver_s *dev,
  *
  ****************************************************************************/
 
-int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback)
+static int devif_poll_connections(FAR struct net_driver_s *dev,
+                                  devif_poll_callback_t callback)
 {
-  int bstop = false;
+  int bstop;
 
   /* Traverse all of the active packet connections and perform the poll
    * action.
@@ -772,6 +769,217 @@ int devif_poll(FAR struct net_driver_s *dev, 
devif_poll_callback_t callback)
   return bstop;
 }
 
+/****************************************************************************
+ * Name: devif_iob_poll
+ *
+ * Description:
+ *   This function will traverse each active network connection structure and
+ *   will perform network polling operations. devif_poll() may be called
+ *   asynchronously with the network driver can accept another outgoing
+ *   packet.
+ *
+ *   This function will call the provided callback function for every active
+ *   connection. Polling will continue until all connections have been polled
+ *   or until the user-supplied function returns a non-zero value (which it
+ *   should do only if it cannot accept further write data).
+ *
+ *   When the callback function is called, there may be an outbound packet
+ *   waiting for service in the device packet buffer, and if so the d_len
+ *   field is set to a value larger than zero. The device driver should then
+ *   send out the packet.
+ *
+ *   This is the iob buffer version of devif_input(),
+ *   this function will support send/receive iob vectors directly between
+ *   the driver and l3/l4 stack to avoid unnecessary memory copies,
+ *   especially on hardware that supports Scatter/gather, which can
+ *   greatly improve performance
+ *   this function will uses d_iob as packets input which used by some
+ *   NICs such as celluler net driver.
+ *
+ *   If NIC hardware support Scatter/gather transfer
+ *
+ *                  tcp_poll()/udp_poll()/pkt_poll()/...(l3/l4)
+ *                             /           \
+ *                            /             \
+ *  devif_poll_[l3|l4]_connections()  devif_iob_send() (nocopy:udp/icmp/...)
+ *             /                                \      (copy:tcp)
+ *            /                                  \
+ *    devif_iob_poll("NIC"_txpoll)             callback() // "NIC"_txpoll
+ *
+ *
+ * Assumptions:
+ *   This function is called from the MAC device driver with the network
+ *   locked.
+ *
+ ****************************************************************************/
+
+static int devif_iob_poll(FAR struct net_driver_s *dev,
+                          devif_poll_callback_t callback)
+{
+  int bstop;
+
+  /* Device polling, prepare iob buffer */
+
+  if (netdev_iob_prepare(dev, false, 0) != OK)
+    {
+      return true;
+    }
+
+  /* Perform all connections poll */
+
+  bstop = devif_poll_connections(dev, callback);
+
+  /* Device polling completed, release iob */
+
+  netdev_iob_release(dev);
+
+  dev->d_buf = NULL;
+
+  return bstop;
+}
+
+/****************************************************************************
+ * Name: devif_poll_callback
+ *
+ * Description:
+ *   This function will help us to gather multiple iob memory slices into a
+ *   linear device buffer. if devices with small memory, this function will
+ *   trigger a memory copy if net device start transmit the iob slices to
+ *   flat buffer
+ *
+ ****************************************************************************/
+
+static int devif_poll_callback(FAR struct net_driver_s *dev)
+{
+  if (dev->d_len > 0)
+    {
+      return true;
+    }
+
+  return false;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devif_poll
+ *
+ * Description:
+ *   This function will traverse each active network connection structure and
+ *   will perform network polling operations. devif_poll() may be called
+ *   asynchronously with the network driver can accept another outgoing
+ *   packet.
+ *
+ *   This function will call the provided callback function for every active
+ *   connection. Polling will continue until all connections have been polled
+ *   or until the user-supplied function returns a non-zero value (which it
+ *   should do only if it cannot accept further write data).
+ *
+ *   When the callback function is called, there may be an outbound packet
+ *   waiting for service in the device packet buffer, and if so the d_len
+ *   field is set to a value larger than zero. The device driver should then
+ *   send out the packet.
+ *
+ *   Compatible with all old flat buffer NICs
+ *
+ *                 tcp_poll()/udp_poll()/pkt_poll()/...(l3|l4)
+ *                            /              \
+ *                           /                \
+ * devif_poll_[l3|l4]_connections()     devif_iob_send() (nocopy:udp/icmp/..)
+ *            /                                   \      (copy:tcp)
+ *           /                                     \
+ *   devif_iob_poll(devif_poll_callback())  devif_poll_callback()
+ *        /                                           \
+ *       /                                             \
+ *  devif_poll("NIC"_txpoll)                     "NIC"_send()(dev->d_buf)
+ *
+ *
+ * Assumptions:
+ *   This function is called from the MAC device driver with the network
+ *   locked.
+ *
+ ****************************************************************************/
+
+int devif_poll(FAR struct net_driver_s *dev, devif_poll_callback_t callback)
+{
+  uint16_t llhdrlen;
+  int bstop = false;
+  FAR uint8_t *buf;
+
+  if (dev->d_buf == NULL)
+    {
+      return devif_iob_poll(dev, callback);
+    }
+
+  buf = dev->d_buf;
+
+  /* Device polling, prepare iob buffer */
+
+  if (netdev_iob_prepare(dev, false, 0) != OK)
+    {
+      return true;
+    }
+
+  llhdrlen = NET_LL_HDRLEN(dev);
+
+  do
+    {
+      /* Perform all connections poll */
+
+      bstop = devif_poll_connections(dev, devif_poll_callback);
+      if (dev->d_len > 0)
+        {
+          /* Copy iob to flat buffer */
+
+          iob_copyout(buf + llhdrlen,
+                      dev->d_iob, dev->d_len, 0);
+
+          /* Copy l2 header (arp out) */
+
+          memcpy(buf, dev->d_iob->io_data +
+                 (CONFIG_NET_LL_GUARDSIZE - llhdrlen), llhdrlen);
+
+          /* Restore flat buffer pointer */
+
+          dev->d_buf = buf;
+
+          /* Call the real device callback */
+
+          bstop = callback(dev);
+
+          /* Flat buffer changed by NIC ? */
+
+          if (dev->d_buf != buf)
+            {
+              if (dev->d_buf == NULL)
+                {
+                  break;
+                }
+
+              buf = dev->d_buf;
+            }
+
+          /* Finish copy, reset iob */
+
+          netdev_iob_prepare(dev, false, 0);
+          iob_update_pktlen(dev->d_iob, 0);
+        }
+    }
+  while (bstop);
+
+  /* Device polling completed, release iob */
+
+  netdev_iob_release(dev);
+
+  /* Restore the flat buffer */
+
+  dev->d_buf = buf;
+
+  return bstop;
+}
+
 /****************************************************************************
  * Name: devif_out
  *
diff --git a/net/devif/devif_send.c b/net/devif/devif_send.c
index 11841b41ed..020e843909 100644
--- a/net/devif/devif_send.c
+++ b/net/devif/devif_send.c
@@ -65,10 +65,23 @@
  *
  ****************************************************************************/
 
-void devif_send(struct net_driver_s *dev, const void *buf, int len)
+void devif_send(FAR struct net_driver_s *dev, FAR const void *buf,
+                int len, unsigned int offset)
 {
-  DEBUGASSERT(dev != NULL && len > 0 && len < NETDEV_PKTSIZE(dev));
+  unsigned int limit = NETDEV_PKTSIZE(dev) -
+                       CONFIG_NET_LL_GUARDSIZE - offset;
 
-  memcpy(dev->d_appdata, buf, len);
-  dev->d_sndlen = len;
+  if (dev == NULL || len == 0 || len > limit)
+    {
+      nerr("ERROR: devif_send fail: %p, sndlen: %u, pktlen: %u\n",
+           dev, len, limit);
+      return;
+    }
+
+  iob_update_pktlen(dev->d_iob, offset);
+
+  /* Copy in iob to target device buffer */
+
+  dev->d_sndlen = iob_copyin(dev->d_iob, buf, len, offset, false) == len ?
+                  len : 0;
 }
diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c
index 1afec12ad5..d95974870f 100644
--- a/net/devif/ipv4_input.c
+++ b/net/devif/ipv4_input.c
@@ -116,13 +116,23 @@
  ****************************************************************************/
 
 /****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-/****************************************************************************
- * Name: ipv4_input
+ * Name: ipv4_in
  *
  * Description:
+ *   Receive an IPv4 packet from the network device.  Verify and forward to
+ *   L3 packet handling logic if the packet is destined for us.
+ *
+ *   This is the iob buffer version of ipv4_input(),
+ *   this function will support send/receive iob vectors directly between
+ *   the driver and l3/l4 stack to avoid unnecessary memory copies,
+ *   especially on hardware that supports Scatter/gather, which can
+ *   greatly improve performance
+ *   this function will uses d_iob as packets input which used by some
+ *   NICs such as celluler net driver.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the packet was received and which contains
+ *           the IPv4 packet.
  *
  * Returned Value:
  *   OK    - The packet was processed (or dropped) and can be discarded.
@@ -133,11 +143,10 @@
  *
  ****************************************************************************/
 
-int ipv4_input(FAR struct net_driver_s *dev)
+static int ipv4_in(FAR struct net_driver_s *dev)
 {
   FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
   in_addr_t destipaddr;
-  uint16_t llhdrlen;
   uint16_t totlen;
   int ret = OK;
 
@@ -175,15 +184,12 @@ int ipv4_input(FAR struct net_driver_s *dev)
 
   /* Get the size of the packet minus the size of link layer header */
 
-  llhdrlen = NET_LL_HDRLEN(dev);
-  if ((llhdrlen + IPv4_HDRLEN) > dev->d_len)
+  if (IPv4_HDRLEN > dev->d_len)
     {
       nwarn("WARNING: Packet shorter than IPv4 header\n");
       goto drop;
     }
 
-  dev->d_len -= llhdrlen;
-
   /* Make sure that all packet processing logic knows that there is an IPv4
    * packet in the device buffer.
    */
@@ -198,11 +204,12 @@ int ipv4_input(FAR struct net_driver_s *dev)
    */
 
   totlen = (ipv4->len[0] << 8) + ipv4->len[1];
-  if (totlen <= dev->d_len)
+  if (totlen < dev->d_len)
     {
+      iob_update_pktlen(dev->d_iob, totlen);
       dev->d_len = totlen;
     }
-  else
+  else if (totlen > dev->d_len)
     {
       nwarn("WARNING: IP packet shorter than length in IP header\n");
       goto drop;
@@ -420,4 +427,52 @@ drop:
   dev->d_len = 0;
   return OK;
 }
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv4_input
+ *
+ * Description:
+ *   Receive an IPv4 packet from the network device.  Verify and forward to
+ *   L3 packet handling logic if the packet is destined for us.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the packet was received and which contains
+ *           the IPv4 packet.
+ *
+ * Returned Value:
+ *   OK    - The packet was processed (or dropped) and can be discarded.
+ *   ERROR - Hold the packet and try again later.  There is a listening
+ *           socket but no receive in place to catch the packet yet.  The
+ *           device's d_len will be set to zero in this case as there is
+ *           no outgoing data.
+ *
+ ****************************************************************************/
+
+int ipv4_input(FAR struct net_driver_s *dev)
+{
+  FAR uint8_t *buf;
+  int ret;
+
+  if (dev->d_iob != NULL)
+    {
+      buf = dev->d_buf;
+
+      /* Set the device buffer to l2 */
+
+      dev->d_buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE -
+                                        NET_LL_HDRLEN(dev)];
+      ret = ipv4_in(dev);
+
+      dev->d_buf = buf;
+
+      return ret;
+    }
+
+  return netdev_input(dev, ipv4_in, true);
+}
+
 #endif /* CONFIG_NET_IPv4 */
diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c
index 94a2130d94..914ff43872 100644
--- a/net/devif/ipv6_input.c
+++ b/net/devif/ipv6_input.c
@@ -185,16 +185,20 @@ static bool check_destipaddr(FAR struct net_driver_s *dev,
 }
 
 /****************************************************************************
- * Public Functions
- ****************************************************************************/
-
-/****************************************************************************
- * Name: ipv6_input
+ * Name: ipv6_in
  *
  * Description:
  *   Receive an IPv6 packet from the network device.  Verify and forward to
  *   L3 packet handling logic if the packet is destined for us.
  *
+ *   This is the iob buffer version of ipv6_input(),
+ *   this function will support send/receive iob vectors directly between
+ *   the driver and l3/l4 stack to avoid unnecessary memory copies,
+ *   especially on hardware that supports Scatter/gather, which can
+ *   greatly improve performance
+ *   this function will uses d_iob as packets input which used by some
+ *   NICs such as celluler net driver.
+ *
  * Input Parameters:
  *   dev   - The device on which the packet was received and which contains
  *           the IPv6 packet.
@@ -214,11 +218,10 @@ static bool check_destipaddr(FAR struct net_driver_s *dev,
  *
  ****************************************************************************/
 
-int ipv6_input(FAR struct net_driver_s *dev)
+static int ipv6_in(FAR struct net_driver_s *dev)
 {
   FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
   FAR uint8_t *payload;
-  uint16_t llhdrlen;
   uint16_t iphdrlen;
   uint16_t paylen;
   uint8_t nxthdr;
@@ -251,15 +254,12 @@ int ipv6_input(FAR struct net_driver_s *dev)
 
   /* Get the size of the packet minus the size of link layer header */
 
-  llhdrlen = NET_LL_HDRLEN(dev);
-  if ((llhdrlen + IPv6_HDRLEN) > dev->d_len)
+  if (IPv6_HDRLEN > dev->d_len)
     {
       nwarn("WARNING: Packet shorter than IPv6 header\n");
       goto drop;
     }
 
-  dev->d_len -= llhdrlen;
-
   /* Make sure that all packet processing logic knows that there is an IPv6
    * packet in the device buffer.
    */
@@ -287,11 +287,12 @@ int ipv6_input(FAR struct net_driver_s *dev)
   paylen = ((uint16_t)ipv6->len[0] << 8) + (uint16_t)ipv6->len[1] +
            IPv6_HDRLEN;
 
-  if (paylen <= dev->d_len)
+  if (paylen < dev->d_len)
     {
+      iob_update_pktlen(dev->d_iob, paylen);
       dev->d_len = paylen;
     }
-  else
+  else if (paylen > dev->d_len)
     {
       nwarn("WARNING: IP packet shorter than length in IP header\n");
       goto drop;
@@ -525,4 +526,57 @@ drop:
   dev->d_len = 0;
   return OK;
 }
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv6_input
+ *
+ * Description:
+ *   Receive an IPv6 packet from the network device.  Verify and forward to
+ *   L3 packet handling logic if the packet is destined for us.
+ *
+ * Input Parameters:
+ *   dev   - The device on which the packet was received and which contains
+ *           the IPv6 packet.
+ * Returned Value:
+ *   OK    - The packet was processed (or dropped) and can be discarded.
+ *   ERROR - Hold the packet and try again later.  There is a listening
+ *           socket but no receive in place to catch the packet yet.  The
+ *           device's d_len will be set to zero in this case as there is
+ *           no outgoing data.
+ *
+ *   If this function returns to the network driver with dev->d_len > 0,
+ *   that is an indication to the driver that there is an outgoing response
+ *   to this input.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+int ipv6_input(FAR struct net_driver_s *dev)
+{
+  FAR uint8_t *buf;
+  int ret;
+
+  if (dev->d_iob != NULL)
+    {
+      buf = dev->d_buf;
+
+      /* Set the device buffer to l2 */
+
+      dev->d_buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE -
+                                        NET_LL_HDRLEN(dev)];
+      ret = ipv6_in(dev);
+
+      dev->d_buf = buf;
+
+      return ret;
+    }
+
+  return netdev_input(dev, ipv6_in, true);
+}
 #endif /* CONFIG_NET_IPv6 */
diff --git a/net/icmp/icmp_input.c b/net/icmp/icmp_input.c
index e44089a158..9e0be69b90 100644
--- a/net/icmp/icmp_input.c
+++ b/net/icmp/icmp_input.c
@@ -100,28 +100,14 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s 
*dev,
   FAR struct ipv4_hdr_s *ipv4;
   struct sockaddr_in inaddr;
   FAR struct iob_s *iob;
-  uint16_t offset;
-  uint16_t buflen;
   uint16_t iphdrlen;
-  uint8_t addrsize;
+  uint16_t buflen;
   int ret;
 
-  /* Try to allocate on I/O buffer to start the chain without waiting (and
-   * throttling as necessary).  If we would have to wait, then drop the
-   * packet.
-   */
-
-  iob = iob_tryalloc(true);
-  if (iob == NULL)
-    {
-      nerr("ERROR: Failed to create new I/O buffer chain\n");
-      goto drop;
-    }
-
   /* Put the IPv4 address at the beginning of the read-ahead buffer */
 
-  ipv4 = IPv4BUF;
-
+  iob               = dev->d_iob;
+  ipv4              = IPv4BUF;
   inaddr.sin_family = AF_INET;
   inaddr.sin_port   = 0;
 
@@ -129,57 +115,23 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s 
*dev,
                     net_ip4addr_conv32(ipv4->srcipaddr));
   memset(inaddr.sin_zero, 0, sizeof(inaddr.sin_zero));
 
-  /* Copy the src address info into the I/O buffer chain.  We will not wait
-   * for an I/O buffer to become available in this context.  It there is
-   * any failure to allocated, the entire I/O buffer chain will be discarded.
-   */
-
-  addrsize = sizeof(struct sockaddr_in);
-  ret      = iob_trycopyin(iob, &addrsize, sizeof(uint8_t), 0, true);
-  if (ret < 0)
-    {
-      /* On a failure, iob_trycopyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to length to the I/O buffer chain: %d\n", ret);
-      goto drop_with_chain;
-    }
-
-  offset = sizeof(uint8_t);
-
-  ret = iob_trycopyin(iob, (FAR const uint8_t *)&inaddr,
-                      sizeof(struct sockaddr_in), offset, true);
-  if (ret < 0)
-    {
-      /* On a failure, iob_trycopyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to source address to the I/O buffer chain: %d\n",
-           ret);
-      goto drop_with_chain;
-    }
-
-  offset += sizeof(struct sockaddr_in);
-
   /* Get the IP header length (accounting for possible options). */
 
   iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2;
 
+  /* Copy the src address info into the front of I/O buffer chain which
+   * overwrites the contents of the packet header field.
+   */
+
+  memcpy(iob->io_data, &inaddr, sizeof(struct sockaddr_in));
+
   /* Copy the new ICMP reply into the I/O buffer chain (without waiting) */
 
   buflen = ICMPSIZE(iphdrlen);
-  ret = iob_trycopyin(iob, IPBUF(iphdrlen), buflen, offset, true);
-  if (ret < 0)
-    {
-      /* On a failure, iob_copyin return a negated error value but does
-       * not free any I/O buffers.
-       */
 
-      nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
-      goto drop_with_chain;
-    }
+  /* Trim l3 header */
+
+  iob = iob_trimhead(iob, iphdrlen);
 
   /* Add the new I/O buffer chain to the tail of the read-ahead queue (again
    * without waiting).
@@ -189,19 +141,18 @@ static uint16_t icmp_datahandler(FAR struct net_driver_s 
*dev,
   if (ret < 0)
     {
       nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
-      goto drop_with_chain;
+      iob_free_chain(iob);
+    }
+  else
+    {
+      ninfo("Buffered %d bytes\n", buflen);
     }
 
-  ninfo("Buffered %d bytes\n", buflen + addrsize + 1);
-  dev->d_len = 0;
-  return buflen;
+  /* Device buffer must be enqueue or freed, clear the handle */
 
-drop_with_chain:
-  iob_free_chain(iob);
+  netdev_iob_clear(dev);
 
-drop:
-  dev->d_len = 0;
-  return 0;
+  return buflen;
 }
 #endif
 
diff --git a/net/icmp/icmp_recvmsg.c b/net/icmp/icmp_recvmsg.c
index 000a5e5bd9..e0f2d4289c 100644
--- a/net/icmp/icmp_recvmsg.c
+++ b/net/icmp/icmp_recvmsg.c
@@ -227,10 +227,8 @@ static inline ssize_t icmp_readahead(FAR struct 
icmp_conn_s *conn,
                                      FAR struct sockaddr_in *from,
                                      FAR socklen_t *fromlen)
 {
-  FAR struct sockaddr_in bitbucket;
   FAR struct iob_s *iob;
   ssize_t ret = -ENODATA;
-  int recvlen;
 
   /* Check there is any ICMP replies already buffered in a read-ahead
    * buffer.
@@ -238,68 +236,26 @@ static inline ssize_t icmp_readahead(FAR struct 
icmp_conn_s *conn,
 
   if ((iob = iob_peek_queue(&conn->readahead)) != NULL)
     {
-      FAR struct iob_s *tmp;
-      uint16_t offset;
-      uint8_t addrsize;
-
       DEBUGASSERT(iob->io_pktlen > 0);
 
-      /* Transfer that buffered data from the I/O buffer chain into
-       * the user buffer.
-       */
-
-      /* First get the size of the address */
-
-      recvlen = iob_copyout(&addrsize, iob, sizeof(uint8_t), 0);
-      if (recvlen != sizeof(uint8_t))
-        {
-          ret = -EIO;
-          goto out;
-        }
-
-      offset = sizeof(uint8_t);
-
-      if (addrsize > sizeof(struct sockaddr_in))
-        {
-          ret = -EINVAL;
-          goto out;
-        }
-
       /* Then get address */
 
-      if (from == NULL)
-        {
-          from = &bitbucket;
-        }
-
-      recvlen = iob_copyout((FAR uint8_t *)from, iob, addrsize, offset);
-      if (recvlen != addrsize)
-        {
-          ret = -EIO;
-          goto out;
-        }
-
-      if (fromlen != NULL)
+      if (from != NULL)
         {
-          *fromlen = addrsize;
+          memcpy(from, iob->io_data, sizeof(struct sockaddr_in));
         }
 
-      offset += addrsize;
-
-      /* And finally, get the buffered data */
+      /* Copy to user */
 
-      ret = (ssize_t)iob_copyout(buf, iob, buflen, offset);
+      ret = (ssize_t)iob_copyout(buf, iob, buflen, 0);
 
       ninfo("Received %ld bytes (of %u)\n", (long)ret, iob->io_pktlen);
 
-out:
       /* Remove the I/O buffer chain from the head of the read-ahead
        * buffer queue.
        */
 
-      tmp = iob_remove_queue(&conn->readahead);
-      DEBUGASSERT(tmp == iob);
-      UNUSED(tmp);
+      iob_remove_queue(&conn->readahead);
 
       /* And free the I/O buffer chain */
 
diff --git a/net/icmp/icmp_reply.c b/net/icmp/icmp_reply.c
index 82b758339b..ad9aed2963 100644
--- a/net/icmp/icmp_reply.c
+++ b/net/icmp/icmp_reply.c
@@ -91,7 +91,7 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, int 
code)
 {
   int ipicmplen = IPv4_HDRLEN + sizeof(struct icmp_hdr_s);
   FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
-  FAR struct icmp_hdr_s *icmp = (FAR struct icmp_hdr_s *)(ipv4 + 1);
+  FAR struct icmp_hdr_s *icmp;
   uint16_t datalen;
 #ifdef CONFIG_NET_BROADCAST
   const in_addr_t bcast = INADDR_BROADCAST;
@@ -119,13 +119,67 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, 
int code)
   if (datalen > ICMP_MAXMSGLEN - ipicmplen)
     {
       datalen = ICMP_MAXMSGLEN - ipicmplen;
+      iob_trimtail(dev->d_iob, dev->d_iob->io_pktlen - datalen);
     }
 
-  dev->d_len = ipicmplen + datalen;
+  /* Save the original datagram */
+
+  if (CONFIG_IOB_BUFSIZE >= datalen + ipicmplen +
+                           CONFIG_NET_LL_GUARDSIZE)
+    {
+      /* Reuse current iob */
+
+      memmove((FAR char *)ipv4 + ipicmplen, ipv4, datalen);
+
+      /* Skip icmp header from iob */
+
+      iob_update_pktlen(dev->d_iob, datalen + ipicmplen);
+    }
+  else
+    {
+      FAR struct iob_s *iob;
+
+      /* Save the original datagram to iob chain */
+
+      iob = dev->d_iob;
+      dev->d_iob = NULL;
+
+      /* Re-prepare device buffer */
 
-  /* Copy fields from original packet */
+      if (netdev_iob_prepare(dev, false, 0) != OK)
+        {
+          dev->d_len = 0;
+          dev->d_iob = iob;
+          netdev_iob_release(dev);
+          return;
+        }
 
-  memmove(icmp + 1, ipv4, datalen);
+      /* Copy ipv4 header to device buffer */
+
+      if (iob_trycopyin(dev->d_iob, (FAR void *)ipv4,
+                        IPv4_HDRLEN, 0, false) != IPv4_HDRLEN)
+        {
+          dev->d_len = 0;
+          netdev_iob_release(dev);
+          iob_free_chain(iob);
+          return;
+        }
+
+      /* Skip icmp header from iob */
+
+      iob_update_pktlen(dev->d_iob, dev->d_iob->io_pktlen +
+                                    sizeof(struct icmp_hdr_s));
+
+      /* Concat new icmp packet before original datagram */
+
+      iob_concat(dev->d_iob, iob);
+
+      /* IPv4 header to new iob */
+
+      ipv4 = IPBUF(0);
+    }
+
+  dev->d_len = ipicmplen + datalen;
 
   ipv4_build_header(IPv4BUF, dev->d_len, IP_PROTO_ICMP,
                     &dev->d_ipaddr, (FAR in_addr_t *)ipv4->srcipaddr,
@@ -133,6 +187,7 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, int 
code)
 
   /* Initialize the ICMP header */
 
+  icmp              = (FAR struct icmp_hdr_s *)(ipv4 + 1);
   icmp->type        = type;
   icmp->icode       = code;
   icmp->data[0]     = 0;
@@ -141,7 +196,7 @@ void icmp_reply(FAR struct net_driver_s *dev, int type, int 
code)
   /* Calculate the ICMP checksum. */
 
   icmp->icmpchksum  = 0;
-  icmp->icmpchksum  = ~icmp_chksum(dev, datalen + sizeof(*icmp));
+  icmp->icmpchksum  = ~icmp_chksum_iob(dev->d_iob);
   if (icmp->icmpchksum == 0)
     {
       icmp->icmpchksum = 0xffff;
diff --git a/net/icmp/icmp_sendmsg.c b/net/icmp/icmp_sendmsg.c
index f3ddcd43cd..7e50f92774 100644
--- a/net/icmp/icmp_sendmsg.c
+++ b/net/icmp/icmp_sendmsg.c
@@ -114,7 +114,11 @@ static void sendto_request(FAR struct net_driver_s *dev,
   /* Copy the ICMP header and payload into place after the IPv4 header */
 
   icmp              = IPBUF(IPv4_HDRLEN);
-  memcpy(icmp, pstate->snd_buf, pstate->snd_buflen);
+
+  iob_update_pktlen(dev->d_iob, IPv4_HDRLEN);
+
+  iob_copyin(dev->d_iob, pstate->snd_buf,
+             pstate->snd_buflen, IPv4_HDRLEN, false);
 
   /* Initialize the IP header. */
 
@@ -125,7 +129,7 @@ static void sendto_request(FAR struct net_driver_s *dev,
   /* Calculate the ICMP checksum. */
 
   icmp->icmpchksum  = 0;
-  icmp->icmpchksum  = ~(icmp_chksum(dev, pstate->snd_buflen));
+  icmp->icmpchksum  = ~icmp_chksum_iob(dev->d_iob);
   if (icmp->icmpchksum == 0)
     {
       icmp->icmpchksum = 0xffff;
diff --git a/net/icmpv6/icmpv6_advertise.c b/net/icmpv6/icmpv6_advertise.c
index 1ca008eacf..decce33189 100644
--- a/net/icmpv6/icmpv6_advertise.c
+++ b/net/icmpv6/icmpv6_advertise.c
@@ -103,6 +103,10 @@ void icmpv6_advertise(FAR struct net_driver_s *dev,
 
   memcpy(adv->tgtlladdr, &dev->d_mac, lladdrsize);
 
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size);
+
   /* Calculate the checksum over both the ICMP header and payload */
 
   adv->chksum    = 0;
diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c
index 24d799ad3a..50ed37d853 100644
--- a/net/icmpv6/icmpv6_input.c
+++ b/net/icmpv6/icmpv6_input.c
@@ -87,82 +87,32 @@ static uint16_t icmpv6_datahandler(FAR struct net_driver_s 
*dev,
                                    unsigned int iplen)
 {
   FAR struct ipv6_hdr_s *ipv6;
-  FAR struct icmpv6_hdr_s *icmpv6;
-  FAR struct iob_s *iob;
   struct sockaddr_in6 inaddr;
-  uint16_t offset;
+  FAR struct iob_s *iob;
   uint16_t buflen;
-  uint8_t addrsize;
   int ret;
 
-  /* Try to allocate on I/O buffer to start the chain without waiting (and
-   * throttling as necessary).  If we would have to wait, then drop the
-   * packet.
-   */
-
-  iob = iob_tryalloc(true);
-  if (iob == NULL)
-    {
-      nerr("ERROR: Failed to create new I/O buffer chain\n");
-      goto drop;
-    }
-
   /* Put the IPv6 address at the beginning of the read-ahead buffer */
 
+  iob                = dev->d_iob;
   ipv6               = IPv6BUF;
   inaddr.sin6_family = AF_INET6;
   inaddr.sin6_port   = 0;
   net_ipv6addr_copy(inaddr.sin6_addr.s6_addr16, ipv6->srcipaddr);
 
-  /* Copy the src address info into the I/O buffer chain.  We will not wait
-   * for an I/O buffer to become available in this context.  It there is
-   * any failure to allocated, the entire I/O buffer chain will be discarded.
+  /* Copy the src address info into the front of I/O buffer chain which
+   * overwrites the contents of the packet header field.
    */
 
-  addrsize = sizeof(struct sockaddr_in6);
-  ret      = iob_trycopyin(iob, &addrsize, sizeof(uint8_t), 0, true);
-  if (ret < 0)
-    {
-      /* On a failure, iob_trycopyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to length to the I/O buffer chain: %d\n", ret);
-      goto drop_with_chain;
-    }
-
-  offset = sizeof(uint8_t);
-
-  ret = iob_trycopyin(iob, (FAR const uint8_t *)&inaddr,
-                      sizeof(struct sockaddr_in6), offset, true);
-  if (ret < 0)
-    {
-      /* On a failure, iob_trycopyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to source address to the I/O buffer chain: %d\n",
-           ret);
-      goto drop_with_chain;
-    }
-
-  offset += sizeof(struct sockaddr_in6);
+  memcpy(iob->io_data, &inaddr, sizeof(struct sockaddr_in6));
 
   /* Copy the new ICMPv6 reply into the I/O buffer chain (without waiting) */
 
   buflen = ICMPv6SIZE;
-  icmpv6 = IPBUF(iplen);
 
-  ret = iob_trycopyin(iob, (FAR uint8_t *)ICMPv6REPLY, buflen, offset, true);
-  if (ret < 0)
-    {
-      /* On a failure, iob_copyin return a negated error value but does
-       * not free any I/O buffers.
-       */
+  /* Trim l3 header */
 
-      nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
-      goto drop_with_chain;
-    }
+  iob = iob_trimhead(iob, iplen);
 
   /* Add the new I/O buffer chain to the tail of the read-ahead queue (again
    * without waiting).
@@ -172,19 +122,17 @@ static uint16_t icmpv6_datahandler(FAR struct 
net_driver_s *dev,
   if (ret < 0)
     {
       nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
-      goto drop_with_chain;
+      iob_free_chain(iob);
+    }
+  else
+    {
+      ninfo("Buffered %d bytes\n", buflen);
     }
 
-  ninfo("Buffered %d bytes\n", buflen + addrsize + 1);
-  dev->d_len = 0;
-  return buflen;
-
-drop_with_chain:
-  iob_free_chain(iob);
+  /* Device buffer must be enqueue or freed, clear the handle */
 
-drop:
-  dev->d_len = 0;
-  return 0;
+  netdev_iob_clear(dev);
+  return buflen;
 }
 #endif
 
diff --git a/net/icmpv6/icmpv6_radvertise.c b/net/icmpv6/icmpv6_radvertise.c
index e818229ca0..0281472687 100644
--- a/net/icmpv6/icmpv6_radvertise.c
+++ b/net/icmpv6/icmpv6_radvertise.c
@@ -193,6 +193,10 @@ void icmpv6_radvertise(FAR struct net_driver_s *dev)
   ipv6addr_mask(prefix->prefix, dev->d_ipv6addr, dev->d_ipv6netmask);
 #endif /* CONFIG_NET_ICMPv6_ROUTER_MANUAL */
 
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size);
+
   /* Calculate the checksum over both the ICMP header and payload */
 
   adv->chksum  = 0;
diff --git a/net/icmpv6/icmpv6_recvmsg.c b/net/icmpv6/icmpv6_recvmsg.c
index 9a805586ad..98e1649580 100644
--- a/net/icmpv6/icmpv6_recvmsg.c
+++ b/net/icmpv6/icmpv6_recvmsg.c
@@ -232,10 +232,8 @@ static inline ssize_t icmpv6_readahead(FAR struct 
icmpv6_conn_s *conn,
                                      FAR struct sockaddr_in6 *from,
                                      FAR socklen_t *fromlen)
 {
-  FAR struct sockaddr_in6 bitbucket;
   FAR struct iob_s *iob;
   ssize_t ret = -ENODATA;
-  int recvlen;
 
   /* Check there is any ICMPv6 replies already buffered in a read-ahead
    * buffer.
@@ -243,68 +241,26 @@ static inline ssize_t icmpv6_readahead(FAR struct 
icmpv6_conn_s *conn,
 
   if ((iob = iob_peek_queue(&conn->readahead)) != NULL)
     {
-      FAR struct iob_s *tmp;
-      uint16_t offset;
-      uint8_t addrsize;
-
       DEBUGASSERT(iob->io_pktlen > 0);
 
-      /* Transfer that buffered data from the I/O buffer chain into
-       * the user buffer.
-       */
-
-      /* First get the size of the address */
-
-      recvlen = iob_copyout(&addrsize, iob, sizeof(uint8_t), 0);
-      if (recvlen != sizeof(uint8_t))
-        {
-          ret = -EIO;
-          goto out;
-        }
-
-      offset = sizeof(uint8_t);
-
-      if (addrsize > sizeof(struct sockaddr_in6))
-        {
-          ret = -EINVAL;
-          goto out;
-        }
-
       /* Then get address */
 
-      if (from == NULL)
-        {
-          from = &bitbucket;
-        }
-
-      recvlen = iob_copyout((FAR uint8_t *)from, iob, addrsize, offset);
-      if (recvlen != addrsize)
-        {
-          ret = -EIO;
-          goto out;
-        }
-
-      if (fromlen != NULL)
+      if (from != NULL)
         {
-          *fromlen = addrsize;
+          memcpy(from, iob->io_data, sizeof(struct sockaddr_in6));
         }
 
-      offset += addrsize;
-
-      /* And finally, get the buffered data */
+      /* Copy to user */
 
-      ret = (ssize_t)iob_copyout(buf, iob, buflen, offset);
+      ret = iob_copyout(buf, iob, buflen, 0);
 
       ninfo("Received %ld bytes (of %u)\n", (long)ret, iob->io_pktlen);
 
-out:
       /* Remove the I/O buffer chain from the head of the read-ahead
        * buffer queue.
        */
 
-      tmp = iob_remove_queue(&conn->readahead);
-      DEBUGASSERT(tmp == iob);
-      UNUSED(tmp);
+      iob_remove_queue(&conn->readahead);
 
       /* And free the I/O buffer chain */
 
diff --git a/net/icmpv6/icmpv6_reply.c b/net/icmpv6/icmpv6_reply.c
index addc3d086f..e67f303c72 100644
--- a/net/icmpv6/icmpv6_reply.c
+++ b/net/icmpv6/icmpv6_reply.c
@@ -86,7 +86,7 @@ void icmpv6_reply(FAR struct net_driver_s *dev, int type, int 
code, int data)
 {
   int ipicmplen = IPv6_HDRLEN + sizeof(struct icmpv6_hdr_s);
   FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
-  FAR struct icmpv6_hdr_s *icmpv6 = (FAR struct icmpv6_hdr_s *)(ipv6 + 1);
+  FAR struct icmpv6_hdr_s *icmpv6;
   uint16_t datalen;
 
   if (net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_unspecaddr)
@@ -108,19 +108,74 @@ void icmpv6_reply(FAR struct net_driver_s *dev, int type, 
int code, int data)
   if (datalen > ICMPv6_MINMTULEN - ipicmplen)
     {
       datalen = ICMPv6_MINMTULEN - ipicmplen;
+      iob_trimtail(dev->d_iob, dev->d_iob->io_pktlen - datalen);
     }
 
-  dev->d_len = ipicmplen + datalen;
+  /* Save the original datagram */
+
+  if (CONFIG_IOB_BUFSIZE >= datalen + ipicmplen +
+                            CONFIG_NET_LL_GUARDSIZE)
+    {
+      /* Reuse current iob */
+
+      memmove((FAR char *)ipv6 + ipicmplen, ipv6, datalen);
+
+      /* Skip icmp header from iob */
+
+      iob_update_pktlen(dev->d_iob, datalen + ipicmplen);
+    }
+  else
+    {
+      FAR struct iob_s *iob;
+
+      /* Save the original datagram to iob chain */
+
+      iob = dev->d_iob;
+      dev->d_iob = NULL;
+
+      /* Re-prepare device buffer */
 
-  /* Copy fields from original packet */
+      if (netdev_iob_prepare(dev, false, 0) != OK)
+        {
+          dev->d_len = 0;
+          dev->d_iob = iob;
+          netdev_iob_release(dev);
+          return;
+        }
 
-  memmove(icmpv6 + 1, ipv6, datalen);
+      /* Copy ipv4 header to device buffer */
+
+      if (iob_trycopyin(dev->d_iob, (FAR void *)ipv6,
+                        IPv6_HDRLEN, 0, false) != IPv6_HDRLEN)
+        {
+          dev->d_len = 0;
+          netdev_iob_release(dev);
+          iob_free_chain(iob);
+          return;
+        }
+
+      /* Skip icmp header from iob */
+
+      iob_update_pktlen(dev->d_iob, dev->d_iob->io_pktlen +
+                                    sizeof(struct icmpv6_hdr_s));
+
+      /* Concat new icmp packet before original datagram */
+
+      iob_concat(dev->d_iob, iob);
+
+      /* IPv6 header to new iob */
+
+      ipv6 = IPBUF(0);
+    }
+
+  dev->d_len = ipicmplen + datalen;
 
   ipv6_build_header(IPv6BUF, dev->d_len - IPv6_HDRLEN, IP_PROTO_ICMP6,
                     dev->d_ipv6addr, ipv6->srcipaddr, 255);
 
   /* Initialize the ICMPv6 header */
 
+  icmpv6          = (FAR struct icmpv6_hdr_s *)(ipv6 + 1);
   icmpv6->type    = type;
   icmpv6->code    = code;
   icmpv6->data[0] = htons(data >> 16);
diff --git a/net/icmpv6/icmpv6_rsolicit.c b/net/icmpv6/icmpv6_rsolicit.c
index 253d3cb0a9..117ebbd02e 100644
--- a/net/icmpv6/icmpv6_rsolicit.c
+++ b/net/icmpv6/icmpv6_rsolicit.c
@@ -97,6 +97,10 @@ void icmpv6_rsolicit(FAR struct net_driver_s *dev)
 
   memcpy(sol->srclladdr, &dev->d_mac, lladdrsize);
 
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size);
+
   /* Calculate the checksum over both the ICMP header and payload */
 
   sol->chksum   = 0;
diff --git a/net/icmpv6/icmpv6_sendmsg.c b/net/icmpv6/icmpv6_sendmsg.c
index 12336e1ef8..70bd8eefa1 100644
--- a/net/icmpv6/icmpv6_sendmsg.c
+++ b/net/icmpv6/icmpv6_sendmsg.c
@@ -116,7 +116,11 @@ static void sendto_request(FAR struct net_driver_s *dev,
   /* Copy the ICMPv6 request and payload into place after the IPv6 header */
 
   icmpv6         = IPBUF(IPv6_HDRLEN);
-  memcpy(icmpv6, pstate->snd_buf, pstate->snd_buflen);
+
+  iob_update_pktlen(dev->d_iob, IPv6_HDRLEN);
+
+  iob_copyin(dev->d_iob, pstate->snd_buf,
+             pstate->snd_buflen, IPv6_HDRLEN, false);
 
   /* Calculate the ICMPv6 checksum over the ICMPv6 header and payload. */
 
diff --git a/net/icmpv6/icmpv6_solicit.c b/net/icmpv6/icmpv6_solicit.c
index e4c22445fa..3fd2b7a463 100644
--- a/net/icmpv6/icmpv6_solicit.c
+++ b/net/icmpv6/icmpv6_solicit.c
@@ -120,6 +120,10 @@ void icmpv6_solicit(FAR struct net_driver_s *dev,
 
   memcpy(sol->srclladdr, &dev->d_mac, lladdrsize);
 
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, IPv6_HDRLEN + l3size);
+
   /* Calculate the checksum over both the ICMP header and payload */
 
   sol->chksum   = 0;
diff --git a/net/igmp/igmp_send.c b/net/igmp/igmp_send.c
index 1146e81ea9..c15a695f77 100644
--- a/net/igmp/igmp_send.c
+++ b/net/igmp/igmp_send.c
@@ -120,6 +120,10 @@ void igmp_send(FAR struct net_driver_s *dev, FAR struct 
igmp_group_s *group,
 
   dev->d_len        = iphdrlen + IGMP_HDRLEN;
 
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, dev->d_len);
+
   /* The total size of the data is the size of the IGMP header */
 
   dev->d_sndlen     = IGMP_HDRLEN;
diff --git a/net/ipforward/ipv4_forward.c b/net/ipforward/ipv4_forward.c
index 3ad5114459..b7e7e14b16 100644
--- a/net/ipforward/ipv4_forward.c
+++ b/net/ipforward/ipv4_forward.c
@@ -265,35 +265,9 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
     }
 #endif
 
-  /* Try to allocate the head of an IOB chain.  If this fails, the
-   * packet will be dropped; we are not operating in a context
-   * where waiting for an IOB is a good idea
-   */
-
-  fwd->f_iob = iob_tryalloc(false);
-  if (fwd->f_iob == NULL)
-    {
-      nwarn("WARNING: iob_tryalloc() failed\n");
-      ret = -ENOMEM;
-      goto errout_with_fwd;
-    }
-
-  /* Copy the L2/L3 headers plus any following payload into an IOB chain.
-   * iob_trycopin() will not wait, but will fail there are no available
-   * IOBs.
-   *
-   * REVISIT: Consider an alternative design that does not require data
-   * copying.  This would require a pool of d_buf's that are managed by
-   * the network rather than the network device.
-   */
+  /* Relay the device buffer */
 
-  ret = iob_trycopyin(fwd->f_iob, (FAR const uint8_t *)ipv4,
-                      dev->d_len, 0, false);
-  if (ret < 0)
-    {
-      nwarn("WARNING: iob_trycopyin() failed: %d\n", ret);
-      goto errout_with_iobchain;
-    }
+  fwd->f_iob = dev->d_iob;
 
   /* Decrement the TTL in the copy of the IPv4 header (retaining the
    * original TTL in the source to handle the broadcast case).  If the
@@ -305,7 +279,7 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
     {
       nwarn("WARNING: Hop limit exceeded... Dropping!\n");
       ret = -EMULTIHOP;
-      goto errout_with_iobchain;
+      goto errout_with_fwd;
     }
 
 #ifdef CONFIG_NET_NAT
@@ -317,7 +291,7 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
   if (ret < 0)
     {
       nwarn("WARNING: Performing NAT outbound failed, dropping!\n");
-      goto errout_with_iobchain;
+      goto errout_with_fwd;
     }
 #endif
 
@@ -326,16 +300,10 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
   ret = ipfwd_forward(fwd);
   if (ret >= 0)
     {
-      dev->d_len = 0;
+      netdev_iob_clear(dev);
       return OK;
     }
 
-errout_with_iobchain:
-  if (fwd != NULL && fwd->f_iob != NULL)
-    {
-      iob_free_chain(fwd->f_iob);
-    }
-
 errout_with_fwd:
   if (fwd != NULL)
     {
diff --git a/net/ipforward/ipv6_forward.c b/net/ipforward/ipv6_forward.c
index 035cf4e8ff..6e6fdb0244 100644
--- a/net/ipforward/ipv6_forward.c
+++ b/net/ipforward/ipv6_forward.c
@@ -399,35 +399,9 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev,
         }
 #endif
 
-      /* Try to allocate the head of an IOB chain.  If this fails, the
-       * packet will be dropped; we are not operating in a context where
-       * waiting for an IOB is a good idea
-       */
-
-      fwd->f_iob = iob_tryalloc(false);
-      if (fwd->f_iob == NULL)
-        {
-          nwarn("WARNING: iob_tryalloc() failed\n");
-          ret = -ENOMEM;
-          goto errout_with_fwd;
-        }
+      /* Relay the device buffer */
 
-      /* Copy the L2/L3 headers plus any following payload into an IOB
-       * chain.  iob_trycopin() will not wait, but will fail there are no
-       * available IOBs.
-       *
-       * REVISIT: Consider an alternative design that does not require data
-       * copying.  This would require a pool of d_buf's that are managed by
-       * the network rather than the network device.
-       */
-
-      ret = iob_trycopyin(fwd->f_iob, (FAR const uint8_t *)ipv6,
-                          dev->d_len, 0, false);
-      if (ret < 0)
-        {
-          nwarn("WARNING: iob_trycopyin() failed: %d\n", ret);
-          goto errout_with_iobchain;
-        }
+      fwd->f_iob = dev->d_iob;
 
       /* Decrement the TTL in the copy of the IPv6 header (retaining the
        * original TTL in the sourcee to handle the broadcast case).  If the
@@ -439,7 +413,7 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev,
         {
           nwarn("WARNING: Hop limit exceeded... Dropping!\n");
           ret = -EMULTIHOP;
-          goto errout_with_iobchain;
+          goto errout_with_fwd;
         }
 
       /* Then set up to forward the packet according to the protocol. */
@@ -447,17 +421,11 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev,
       ret = ipfwd_forward(fwd);
       if (ret >= 0)
         {
-          dev->d_len = 0;
+          netdev_iob_clear(dev);
           return OK;
         }
     }
 
-errout_with_iobchain:
-  if (fwd != NULL && fwd->f_iob != NULL)
-    {
-      iob_free_chain(fwd->f_iob);
-    }
-
 errout_with_fwd:
   if (fwd != NULL)
     {
diff --git a/net/mld/mld_send.c b/net/mld/mld_send.c
index 60903686c8..30de73e732 100644
--- a/net/mld/mld_send.c
+++ b/net/mld/mld_send.c
@@ -159,6 +159,10 @@ void mld_send(FAR struct net_driver_s *dev, FAR struct 
mld_group_s *group,
 
   dev->d_sndlen  = RASIZE + mldsize;
 
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, dev->d_len);
+
   /* Select the IPv6 destination address.
    * This varies with the type of message being sent:
    *
diff --git a/net/neighbor/neighbor_ethernet_out.c 
b/net/neighbor/neighbor_ethernet_out.c
index 496c892778..f8246498d1 100644
--- a/net/neighbor/neighbor_ethernet_out.c
+++ b/net/neighbor/neighbor_ethernet_out.c
@@ -196,6 +196,10 @@ void neighbor_ethernet_out(FAR struct net_driver_s *dev)
   memcpy(eth->src, dev->d_mac.ether.ether_addr_octet, ETHER_ADDR_LEN);
   eth->type  = HTONS(ETHTYPE_IP6);
 
+  /* Update device buffer length */
+
+  iob_update_pktlen(dev->d_iob, dev->d_len);
+
   /* Add the size of the layer layer header to the total size of the
    * outgoing packet.
    */
diff --git a/net/netdev/Make.defs b/net/netdev/Make.defs
index 31c3c786d5..ace44637da 100644
--- a/net/netdev/Make.defs
+++ b/net/netdev/Make.defs
@@ -26,6 +26,10 @@ NETDEV_CSRCS += netdev_count.c netdev_ifconf.c 
netdev_foreach.c
 NETDEV_CSRCS += netdev_unregister.c netdev_carrier.c netdev_default.c
 NETDEV_CSRCS += netdev_verify.c netdev_lladdrsize.c
 
+ifeq ($(CONFIG_MM_IOB),y)
+NETDEV_CSRCS += netdev_input.c netdev_iob.c
+endif
+
 ifeq ($(CONFIG_NETDOWN_NOTIFIER),y)
 SOCK_CSRCS += netdown_notifier.c
 endif
diff --git a/net/netdev/netdev.h b/net/netdev/netdev.h
index 34ba13d6c5..ba5e393a3c 100644
--- a/net/netdev/netdev.h
+++ b/net/netdev/netdev.h
@@ -31,6 +31,7 @@
 #include <stdbool.h>
 
 #include <nuttx/net/ip.h>
+#include <nuttx/net/netdev.h>
 
 #ifdef CONFIG_NETDOWN_NOTIFIER
 #  include <nuttx/wqueue.h>
diff --git a/net/netdev/netdev_input.c b/net/netdev/netdev_input.c
new file mode 100644
index 0000000000..a2669a4c98
--- /dev/null
+++ b/net/netdev/netdev_input.c
@@ -0,0 +1,117 @@
+/****************************************************************************
+ * net/netdev/netdev_input.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <nuttx/net/netdev.h>
+
+#include "utils/utils.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netdev_input
+ *
+ * Description:
+ *   This function will copy the flat buffer that does not support
+ *   Scatter/gather to the iob vector buffer.
+ *
+ *   Compatible with all old flat buffer NICs:
+ *
+ *   [tcp|udp|icmp|...]ipv[4|6]_data_handler()
+ *                     |                    (iob_concat/append to readahead)
+ *                     |
+ *              pkt/ipv[4/6]_in()/...
+ *                     |
+ *                     |
+ *                netdev_input()  // new interface, Scatter/gather flat/iobs
+ *                     |
+ *                     |
+ *           pkt/ipv[4|6]_input()/...
+ *                     |
+ *                     |
+ *     NICs io vector receive(Orignal flat buffer)
+ *
+ * Input Parameters:
+ *   NULL
+ *
+ * Returned Value:
+ *  Pointer to default network driver on success; null on failure
+ *
+ ****************************************************************************/
+
+int netdev_input(FAR struct net_driver_s *dev,
+                 devif_poll_callback_t callback, bool reply)
+{
+  uint16_t llhdrlen = NET_LL_HDRLEN(dev);
+  unsigned int offset = CONFIG_NET_LL_GUARDSIZE - llhdrlen;
+  FAR uint8_t *buf = dev->d_buf;
+  unsigned int l3l4len;
+  int ret;
+
+  /* Prepare iob buffer */
+
+  ret = netdev_iob_prepare(dev, false, 0);
+  if (ret != OK)
+    {
+      return ret;
+    }
+
+  /* Copy l2 header to gruard area */
+
+  memcpy(dev->d_iob->io_data + offset, buf, llhdrlen);
+
+  /* Copy l3/l4 data to iob entry */
+
+  l3l4len = dev->d_len - llhdrlen;
+
+  ret = iob_trycopyin(dev->d_iob, buf + llhdrlen,
+                      l3l4len, 0, false);
+  if (ret == l3l4len)
+    {
+      /* Update device buffer to l2 start */
+
+      dev->d_buf = dev->d_iob->io_data + offset;
+
+      iob_update_pktlen(dev->d_iob, l3l4len);
+
+      ret = callback(dev);
+      if (dev->d_iob != NULL && reply)
+        {
+          if (ret == OK && dev->d_len > 0)
+            {
+              iob_copyout(buf + llhdrlen, dev->d_iob, dev->d_len, 0);
+              memcpy(buf, dev->d_iob->io_data + offset, llhdrlen);
+            }
+        }
+    }
+
+  netdev_iob_release(dev);
+
+  dev->d_buf = buf;
+
+  return ret;
+}
diff --git a/net/netdev/netdev_iob.c b/net/netdev/netdev_iob.c
new file mode 100644
index 0000000000..1ed32a0d2f
--- /dev/null
+++ b/net/netdev/netdev_iob.c
@@ -0,0 +1,128 @@
+/****************************************************************************
+ * net/netdev/netdev_iob.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include <errno.h>
+
+#include <nuttx/net/netdev.h>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: netdev_iob_prepare
+ *
+ * Description:
+ *   Prepare data buffer for a given NIC
+ *   The iob offset will be updated to l2 gruard size by default:
+ *  ----------------------------------------------------------------
+ *  |                     iob entry                                |
+ *  ---------------------------------------------------------------|
+ *  |<--- CONFIG_NET_LL_GUARDSIZE -->|<--- io_len/io_pktlen(0) --->|
+ *  ---------------------------------------------------------------|
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ * Returned Value:
+ *   A non-zero copy is returned on success.
+ *
+ ****************************************************************************/
+
+int netdev_iob_prepare(FAR struct net_driver_s *dev, bool throttled,
+                       unsigned int timeout)
+{
+  /* Prepare iob buffer */
+
+  if (dev->d_iob == NULL)
+    {
+      dev->d_iob = net_iobtimedalloc(false, timeout);
+      if (dev->d_iob == NULL && throttled)
+        {
+          dev->d_iob = net_iobtimedalloc(true, timeout);
+        }
+    }
+
+  if (dev->d_iob == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  /* Set the device buffer to l2 */
+
+  dev->d_buf = &dev->d_iob->io_data[CONFIG_NET_LL_GUARDSIZE -
+                                    NET_LL_HDRLEN(dev)];
+
+  /* Update l2 gruard size */
+
+  iob_reserve(dev->d_iob, CONFIG_NET_LL_GUARDSIZE);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: netdev_iob_clear
+ *
+ * Description:
+ *   Clean up buffer resources for a given NIC
+ *
+ * Assumptions:
+ *   The caller has locked the network and dev->d_iob has been
+ *   released or taken away.
+ *
+ ****************************************************************************/
+
+void netdev_iob_clear(FAR struct net_driver_s *dev)
+{
+  /* Clear the device buffer */
+
+  dev->d_iob = NULL;
+  dev->d_buf = NULL;
+  dev->d_len = 0;
+}
+
+/****************************************************************************
+ * Name: netdev_iob_release
+ *
+ * Description:
+ *   Release buffer resources for a given NIC
+ *
+ * Assumptions:
+ *   The caller has locked the network.
+ *
+ ****************************************************************************/
+
+void netdev_iob_release(FAR struct net_driver_s *dev)
+{
+  /* Release device buffer */
+
+  if (dev->d_iob != NULL)
+    {
+      iob_free_chain(dev->d_iob);
+      dev->d_iob = NULL;
+    }
+}
diff --git a/net/pkt/pkt_input.c b/net/pkt/pkt_input.c
index d84946be51..f825360a6a 100644
--- a/net/pkt/pkt_input.c
+++ b/net/pkt/pkt_input.c
@@ -36,21 +36,23 @@
 #include "pkt/pkt.h"
 
 /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
-
-#define PKTBUF ((FAR struct eth_hdr_s *)dev->d_buf)
-
-/****************************************************************************
- * Public Functions
+ * Private Functions
  ****************************************************************************/
 
 /****************************************************************************
- * Name: pkt_input
+ * Name: pkt_in
  *
  * Description:
  *   Handle incoming packet input
  *
+ *   This is the iob buffer version of pkt_input(),
+ *   this function will support send/receive iob vectors directly between
+ *   the driver and l3/l4 stack to avoid unnecessary memory copies,
+ *   especially on hardware that supports Scatter/gather, which can
+ *   greatly improve performance
+ *   this function will uses d_iob as packets input which used by some
+ *   NICs such as celluler net driver.
+ *
  * Input Parameters:
  *   dev - The device driver structure containing the received packet
  *
@@ -65,10 +67,10 @@
  *
  ****************************************************************************/
 
-int pkt_input(struct net_driver_s *dev)
+static int pkt_in(FAR struct net_driver_s *dev)
 {
   FAR struct pkt_conn_s *conn;
-  FAR struct eth_hdr_s  *pbuf = PKTBUF;
+  FAR struct eth_hdr_s  *pbuf = ETHBUF;
   int ret = OK;
 
   conn = pkt_active(pbuf);
@@ -109,4 +111,38 @@ int pkt_input(struct net_driver_s *dev)
   return ret;
 }
 
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pkt_input
+ *
+ * Description:
+ *   Handle incoming packet input
+ *
+ * Input Parameters:
+ *   dev - The device driver structure containing the received packet
+ *
+ * Returned Value:
+ *   OK     The packet has been processed  and can be deleted
+ *  -EAGAIN There is a matching connection, but could not dispatch the packet
+ *          yet.  Useful when a packet arrives before a recv call is in
+ *          place.
+ *
+ * Assumptions:
+ *   The network is locked.
+ *
+ ****************************************************************************/
+
+int pkt_input(FAR struct net_driver_s *dev)
+{
+  if (dev->d_iob != NULL)
+    {
+      return pkt_in(dev);
+    }
+
+  return netdev_input(dev, pkt_in, false);
+}
+
 #endif /* CONFIG_NET && CONFIG_NET_PKT */
diff --git a/net/pkt/pkt_recvmsg.c b/net/pkt/pkt_recvmsg.c
index d0e4999586..aa989da875 100644
--- a/net/pkt/pkt_recvmsg.c
+++ b/net/pkt/pkt_recvmsg.c
@@ -115,6 +115,7 @@ static inline void pkt_add_recvlen(FAR struct 
pkt_recvfrom_s *pstate,
 static void pkt_recvfrom_newdata(FAR struct net_driver_s *dev,
                                  FAR struct pkt_recvfrom_s *pstate)
 {
+  unsigned int offset;
   size_t recvlen;
 
   if (dev->d_len > pstate->pr_buflen)
@@ -128,7 +129,10 @@ static void pkt_recvfrom_newdata(FAR struct net_driver_s 
*dev,
 
   /* Copy the new packet data into the user buffer */
 
-  memcpy(pstate->pr_buffer, dev->d_buf, recvlen);
+  offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset;
+
+  recvlen = iob_copyout(pstate->pr_buffer, dev->d_iob, recvlen, offset);
+
   ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len);
 
   /* Update the accumulated size of the data read */
diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h
index a7af8d3cec..73858fea9b 100644
--- a/net/tcp/tcp.h
+++ b/net/tcp/tcp.h
@@ -35,6 +35,7 @@
 #include <nuttx/mm/iob.h>
 #include <nuttx/net/ip.h>
 #include <nuttx/net/net.h>
+#include <nuttx/net/tcp.h>
 #include <nuttx/wqueue.h>
 
 #ifdef CONFIG_NET_TCP
@@ -1250,8 +1251,9 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
  *
  ****************************************************************************/
 
-uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
-                         uint16_t nbytes);
+uint16_t tcp_datahandler(FAR struct net_driver_s *dev,
+                         FAR struct tcp_conn_s *conn,
+                         uint16_t offset);
 
 /****************************************************************************
  * Name: tcp_backlogcreate
@@ -2044,6 +2046,22 @@ int tcp_ioctl(FAR struct tcp_conn_s *conn, int cmd, 
unsigned long arg);
 void tcp_sendbuffer_notify(FAR struct tcp_conn_s *conn);
 #endif /* CONFIG_NET_SEND_BUFSIZE */
 
+/****************************************************************************
+ * Name: tcpip_hdrsize
+ *
+ * Description:
+ *   Get the total size of L3 and L4 TCP header
+ *
+ * Input Parameters:
+ *   conn     The connection structure associated with the socket
+ *
+ * Returned Value:
+ *   the total size of L3 and L4 TCP header
+ *
+ ****************************************************************************/
+
+uint16_t tcpip_hdrsize(FAR struct tcp_conn_s *conn);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/net/tcp/tcp_callback.c b/net/tcp/tcp_callback.c
index 4855c821c0..6d27baa457 100644
--- a/net/tcp/tcp_callback.c
+++ b/net/tcp/tcp_callback.c
@@ -61,13 +61,13 @@ static inline uint16_t
 tcp_data_event(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
                uint16_t flags)
 {
-  uint16_t ret;
+  uint16_t recvlen;
 
   /* Assume that we will ACK the data.  The data will be ACKed if it is
    * placed in the read-ahead buffer -OR- if it zero length
    */
 
-  ret = (flags & ~TCP_NEWDATA) | TCP_SNDACK;
+  flags = (flags & ~TCP_NEWDATA) | TCP_SNDACK;
 
   /* Is there new data?  With non-zero length?  (Certain connection events
    * can have zero-length with TCP_NEWDATA set just to cause an ACK).
@@ -75,46 +75,25 @@ tcp_data_event(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn,
 
   if (dev->d_len > 0)
     {
-      uint8_t *buffer = dev->d_appdata;
-      int      buflen = dev->d_len;
-      uint16_t recvlen;
-
       ninfo("No listener on connection\n");
 
       /* Save as the packet data as in the read-ahead buffer.  NOTE that
        * partial packets will not be buffered.
        */
 
-      recvlen = tcp_datahandler(conn, buffer, buflen);
-      if (recvlen < buflen)
-        {
-          /* There is no handler to receive new data and there are no free
-           * read-ahead buffers to retain the data -- drop the packet.
-           */
-
-          ninfo("Dropped %d/%d bytes\n", buflen - recvlen, buflen);
-
-#ifdef CONFIG_NET_STATISTICS
-          g_netstats.tcp.drop++;
-#endif
-          /* Clear the TCP_SNDACK bit so that no ACK will be sent.
-           * Clear the TCP_CLOSE because we effectively dropped
-           * the FIN as well.
-           *
-           * Revisit: It might make more sense to send a dup ack
-           * to give a hint to the peer.
-           */
-
-          ret &= ~(TCP_SNDACK | TCP_CLOSE);
-        }
+      recvlen = tcp_datahandler(dev, conn,
+                                (dev->d_appdata - dev->d_iob->io_data) -
+                                dev->d_iob->io_offset);
 
       net_incr32(conn->rcvseq, recvlen);
+
+      netdev_iob_clear(dev);
     }
 
   /* In any event, the new data has now been handled */
 
   dev->d_len = 0;
-  return ret;
+  return flags;
 }
 
 /****************************************************************************
@@ -139,6 +118,13 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
   uint16_t orig = flags;
 #endif
 
+  /* Prepare device buffer */
+
+  if (dev->d_iob == NULL && netdev_iob_prepare(dev, true, 0) != OK)
+    {
+      return 0;
+    }
+
   /* Preserve the TCP_ACKDATA, TCP_CLOSE, and TCP_ABORT in the response.
    * These is needed by the network to handle responses and buffer state.
    * The TCP_NEWDATA indication will trigger the ACK response, but must be
@@ -202,6 +188,13 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
     }
 #endif
 
+  /* Re-prepare the device buffer if d_iob is consumed by the stack */
+
+  if (dev->d_iob == NULL)
+    {
+      netdev_iob_prepare(dev, true, 0);
+    }
+
   return flags;
 }
 
@@ -231,84 +224,41 @@ uint16_t tcp_callback(FAR struct net_driver_s *dev,
  *
  ****************************************************************************/
 
-uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR uint8_t *buffer,
-                         uint16_t buflen)
+uint16_t tcp_datahandler(FAR struct net_driver_s *dev,
+                         FAR struct tcp_conn_s *conn,
+                         uint16_t offset)
 {
-  FAR struct iob_s *iob;
-  uint16_t copied = 0;
-  int ret;
-  unsigned int i;
-
-  /* Try to allocate I/O buffers and copy the data into them
-   * without waiting (and throttling as necessary).
-   */
+  FAR struct iob_s *iob = dev->d_iob;
+  uint16_t buflen;
 
-  iob = conn->readahead;
-  for (i = 0; i < 2; i++)
+  if (offset > 0)
     {
-      bool throttled = i == 0; /* try throttled=true first */
-
-      if (!throttled)
-        {
-#if CONFIG_IOB_THROTTLE > 0
-          if (conn->readahead != NULL)
-            {
-              ninfo("Do not use throttled=false because of "
-                    "non-empty readahead\n");
-              break;
-            }
-#else
-          break;
-#endif
-        }
-
-      if (iob == NULL)
-        {
-          iob = iob_tryalloc(throttled);
-          if (iob == NULL)
-            {
-              continue;
-            }
-
-          iob->io_pktlen = 0;
-        }
-
-      if (iob != NULL)
-        {
-          uint32_t olen = iob->io_pktlen;
-
-          ret = iob_trycopyin(iob, buffer + copied, buflen - copied,
-                              olen, throttled);
-          copied += iob->io_pktlen - olen;
-          if (ret < 0)
-            {
-              /* On a failure, iob_copyin return a negated error value but
-               * does not free any I/O buffers.
-               */
-
-              continue;
-            }
-        }
-
-      break;
+      /* Remove 'bufoff' bytes from the beginning of the input I/O chain */
+
+      iob = iob_trimhead(iob, offset);
     }
 
-  DEBUGASSERT(conn->readahead == iob || conn->readahead == NULL);
-  if (iob == NULL)
+  /* Trim tail if l3/l4 header has been removed */
+
+  if (dev->d_len < iob->io_pktlen)
     {
-      nerr("ERROR: Failed to create new I/O buffer chain\n");
-      DEBUGASSERT(copied == 0);
-      return 0;
+      iob = iob_trimtail(iob, iob->io_pktlen - dev->d_len);
     }
 
-  if (copied == 0)
+  buflen = iob->io_pktlen;
+
+  /* Concat the iob to readahead */
+
+  if (conn->readahead == NULL)
     {
-      nerr("ERROR: Failed to append new I/O buffer\n");
-      DEBUGASSERT(conn->readahead == iob);
-      return 0;
+      conn->readahead = iob;
+    }
+  else
+    {
+      iob_concat(conn->readahead, iob);
     }
 
-  conn->readahead = iob;
+  netdev_iob_clear(dev);
 
 #ifdef CONFIG_NET_TCP_NOTIFIER
   /* Provide notification(s) that additional TCP read-ahead data is
@@ -318,8 +268,8 @@ uint16_t tcp_datahandler(FAR struct tcp_conn_s *conn, FAR 
uint8_t *buffer,
   tcp_readahead_signal(conn);
 #endif
 
-  ninfo("Buffered %" PRIu16 " bytes\n", copied);
-  return copied;
+  ninfo("Buffered %" PRIu16 " bytes\n", buflen);
+  return buflen;
 }
 
 #endif /* NET_TCP_HAVE_STACK */
diff --git a/net/tcp/tcp_recvfrom.c b/net/tcp/tcp_recvfrom.c
index 64e42394fe..cc79e5b5dd 100644
--- a/net/tcp/tcp_recvfrom.c
+++ b/net/tcp/tcp_recvfrom.c
@@ -113,6 +113,7 @@ static inline void tcp_update_recvlen(FAR struct 
tcp_recvfrom_s *pstate,
 static size_t tcp_recvfrom_newdata(FAR struct net_driver_s *dev,
                                    FAR struct tcp_recvfrom_s *pstate)
 {
+  unsigned int offset;
   size_t recvlen;
 
   /* Get the length of the data to return */
@@ -128,7 +129,14 @@ static size_t tcp_recvfrom_newdata(FAR struct net_driver_s 
*dev,
 
   /* Copy the new appdata into the user buffer */
 
-  memcpy(pstate->ir_buffer, dev->d_appdata, recvlen);
+  offset = (dev->d_appdata - dev->d_iob->io_data) - dev->d_iob->io_offset;
+
+  recvlen = iob_copyout(pstate->ir_buffer, dev->d_iob, recvlen, offset);
+
+  /* Trim the copied buffers */
+
+  dev->d_iob = iob_trimhead(dev->d_iob, recvlen + offset);
+
   ninfo("Received %d bytes (of %d)\n", (int)recvlen, (int)dev->d_len);
 
   /* Update the accumulated size of the data read */
@@ -172,11 +180,10 @@ static inline uint16_t tcp_newdata(FAR struct 
net_driver_s *dev,
 
   if (recvlen < dev->d_len)
     {
-      FAR uint8_t *buffer = (FAR uint8_t *)dev->d_appdata + recvlen;
-      uint16_t buflen = dev->d_len - recvlen;
       uint16_t nsaved;
+      uint16_t buflen = dev->d_len - recvlen;
 
-      nsaved = tcp_datahandler(conn, buffer, buflen);
+      nsaved = tcp_datahandler(dev, conn, 0);
       if (nsaved < buflen)
         {
           nwarn("WARNING: packet data not fully saved "
@@ -189,6 +196,10 @@ static inline uint16_t tcp_newdata(FAR struct net_driver_s 
*dev,
 
       recvlen += nsaved;
     }
+  else
+    {
+      netdev_iob_release(dev);
+    }
 
   if (recvlen < dev->d_len)
     {
@@ -379,16 +390,16 @@ static uint16_t tcp_recvhandler(FAR struct net_driver_s 
*dev,
 
       if ((flags & TCP_NEWDATA) != 0)
         {
+          /* Save the sender's address in the caller's 'from' location */
+
+          tcp_sender(dev, pstate);
+
           /* Copy the data from the packet (saving any unused bytes from the
            * packet in the read-ahead buffer).
            */
 
           flags = tcp_newdata(dev, pstate, flags);
 
-          /* Save the sender's address in the caller's 'from' location */
-
-          tcp_sender(dev, pstate);
-
           /* Indicate that the data has been consumed and that an ACK
            * should be sent.
            */
diff --git a/net/tcp/tcp_send.c b/net/tcp/tcp_send.c
index 6b1da6190e..2d8f2f297b 100644
--- a/net/tcp/tcp_send.c
+++ b/net/tcp/tcp_send.c
@@ -168,6 +168,10 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
   tcp->urgp[0] = 0;
   tcp->urgp[1] = 0;
 
+  /* Update device buffer length before setup the IP header */
+
+  iob_update_pktlen(dev->d_iob, dev->d_len);
+
   /* Calculate chk & build L3 header */
 
 #ifdef CONFIG_NET_IPv6
@@ -263,8 +267,14 @@ static void tcp_sendcommon(FAR struct net_driver_s *dev,
 void tcp_send(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
               uint16_t flags, uint16_t len)
 {
-  FAR struct tcp_hdr_s *tcp = tcp_header(dev);
+  FAR struct tcp_hdr_s *tcp;
 
+  if (dev->d_iob == NULL)
+    {
+      return;
+    }
+
+  tcp            = tcp_header(dev);
   tcp->flags     = flags;
   dev->d_len     = len;
   tcp->tcpoffset = (TCP_HDRLEN / 4) << 4;
@@ -290,18 +300,25 @@ void tcp_send(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn,
 
 void tcp_reset(FAR struct net_driver_s *dev)
 {
-  FAR struct tcp_hdr_s *tcp = tcp_header(dev);
+  FAR struct tcp_hdr_s *tcp;
   uint32_t ackno;
   uint16_t tmp16;
   uint16_t acklen = 0;
   uint8_t seqbyte;
 
+  if (dev->d_iob == NULL)
+    {
+      return;
+    }
+
 #ifdef CONFIG_NET_STATISTICS
   g_netstats.tcp.rst++;
 #endif
 
   /* TCP setup */
 
+  tcp = tcp_header(dev);
+
   if ((tcp->flags & TCP_SYN) != 0 || (tcp->flags & TCP_FIN) != 0)
     {
       acklen++;
@@ -313,7 +330,12 @@ void tcp_reset(FAR struct net_driver_s *dev)
 #endif
     {
       FAR struct ipv6_hdr_s *ip = IPv6BUF;
+
       acklen += (ip->len[0] << 8 | ip->len[1]);
+
+      /* Set the packet length to the size of the IPv6 + TCP headers */
+
+      dev->d_len = IPv6TCP_HDRLEN;
     }
 #endif /* CONFIG_NET_IPv6 */
 
@@ -323,7 +345,12 @@ void tcp_reset(FAR struct net_driver_s *dev)
 #endif
     {
       FAR struct ipv4_hdr_s *ip = IPv4BUF;
+
       acklen += (ip->len[0] << 8) + ip->len[1] - (ip->vhl & 0x0f) * 4;
+
+      /* Set the packet length to the size of the IPv4 + TCP headers */
+
+      dev->d_len = IPv4TCP_HDRLEN;
     }
 #endif /* CONFIG_NET_IPv4 */
 
@@ -373,6 +400,10 @@ void tcp_reset(FAR struct net_driver_s *dev)
   tcp->urgp[0] = 0;
   tcp->urgp[0] = 0;
 
+  /* Update device buffer length before setup the IP header */
+
+  iob_update_pktlen(dev->d_iob, dev->d_len);
+
   /* Calculate chk & build L3 header */
 
 #ifdef CONFIG_NET_IPv6
@@ -382,10 +413,6 @@ void tcp_reset(FAR struct net_driver_s *dev)
     {
       FAR struct ipv6_hdr_s *ipv6 = IPv6BUF;
 
-      /* Set the packet length to the size of the IPv6 + TCP headers */
-
-      dev->d_len = IPv6TCP_HDRLEN;
-
       ipv6_build_header(ipv6, dev->d_len - IPv6_HDRLEN,
                         IP_PROTO_TCP, dev->d_ipv6addr, ipv6->srcipaddr,
                         IP_TTL_DEFAULT);
@@ -402,10 +429,6 @@ void tcp_reset(FAR struct net_driver_s *dev)
     {
       FAR struct ipv4_hdr_s *ipv4 = IPv4BUF;
 
-      /* Set the packet length to the size of the IPv4 + TCP headers */
-
-      dev->d_len = IPv4TCP_HDRLEN;
-
       ipv4_build_header(IPv4BUF, dev->d_len, IP_PROTO_TCP,
                         &dev->d_ipaddr, (FAR in_addr_t *)ipv4->srcipaddr,
                         IP_TTL_DEFAULT, NULL);
@@ -485,6 +508,11 @@ void tcp_synack(FAR struct net_driver_s *dev, FAR struct 
tcp_conn_s *conn,
   uint16_t tcp_mss;
   int16_t optlen = 0;
 
+  if (dev->d_iob == NULL)
+    {
+      return;
+    }
+
   /* Get values that vary with the underlying IP domain */
 
 #ifdef CONFIG_NET_IPv6
@@ -596,4 +624,44 @@ void tcp_send_txnotify(FAR struct socket *psock,
 #endif /* CONFIG_NET_IPv6 */
 }
 
+/****************************************************************************
+ * Name: tcpip_hdrsize
+ *
+ * Description:
+ *   Get the total size of L3 and L4 TCP header
+ *
+ * Input Parameters:
+ *   conn     The connection structure associated with the socket
+ *
+ * Returned Value:
+ *   the total size of L3 and L4 TCP header
+ *
+ ****************************************************************************/
+
+uint16_t tcpip_hdrsize(FAR struct tcp_conn_s *conn)
+{
+  uint16_t hdrsize = sizeof(struct tcp_hdr_s);
+
+#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
+  if (conn->domain == PF_INET)
+    {
+      /* Select the IPv4 domain */
+
+      return sizeof(struct ipv4_hdr_s) + hdrsize;
+    }
+  else /* if (domain == PF_INET6) */
+    {
+      /* Select the IPv6 domain */
+
+      return sizeof(struct ipv6_hdr_s) + hdrsize;
+    }
+#elif defined(CONFIG_NET_IPv4)
+  ((void)conn);
+  return sizeof(struct ipv4_hdr_s) + hdrsize;
+#elif defined(CONFIG_NET_IPv6)
+  ((void)conn);
+  return sizeof(struct ipv6_hdr_s) + hdrsize;
+#endif
+}
+
 #endif /* CONFIG_NET && CONFIG_NET_TCP */
diff --git a/net/tcp/tcp_send_buffered.c b/net/tcp/tcp_send_buffered.c
index 2387da4a24..6925c25c1d 100644
--- a/net/tcp/tcp_send_buffered.c
+++ b/net/tcp/tcp_send_buffered.c
@@ -596,7 +596,8 @@ static uint16_t psock_send_eventhandler(FAR struct 
net_driver_s *dev,
            * happen until the polling cycle completes).
            */
 
-          devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, 0);
+          devif_iob_send(dev, TCP_WBIOB(wrb), sndlen,
+                         0, tcpip_hdrsize(conn));
 
           /* Reset the retransmission timer. */
 
@@ -882,7 +883,8 @@ static uint16_t psock_send_eventhandler(FAR struct 
net_driver_s *dev,
            * won't actually happen until the polling cycle completes).
            */
 
-          devif_iob_send(dev, TCP_WBIOB(wrb), sndlen, TCP_WBSENT(wrb));
+          devif_iob_send(dev, TCP_WBIOB(wrb), sndlen,
+                         TCP_WBSENT(wrb), tcpip_hdrsize(conn));
 
           /* Remember how much data we send out now so that we know
            * when everything has been acknowledged.  Just increment
diff --git a/net/tcp/tcp_send_unbuffered.c b/net/tcp/tcp_send_unbuffered.c
index efbd7f6c5a..8ddcee4404 100644
--- a/net/tcp/tcp_send_unbuffered.c
+++ b/net/tcp/tcp_send_unbuffered.c
@@ -326,9 +326,8 @@ static uint16_t tcpsend_eventhandler(FAR struct 
net_driver_s *dev,
        * happen until the polling cycle completes).
        */
 
-      devif_send(dev,
-                 &pstate->snd_buffer[pstate->snd_acked],
-                 sndlen);
+      devif_send(dev, &pstate->snd_buffer[pstate->snd_acked],
+                 sndlen, tcpip_hdrsize(conn));
 
       /* Continue waiting */
 
@@ -409,7 +408,8 @@ static uint16_t tcpsend_eventhandler(FAR struct 
net_driver_s *dev,
            * happen until the polling cycle completes).
            */
 
-          devif_send(dev, &pstate->snd_buffer[pstate->snd_sent], sndlen);
+          devif_send(dev, &pstate->snd_buffer[pstate->snd_sent],
+                     sndlen, tcpip_hdrsize(conn));
 
           /* Update the amount of data sent (but not necessarily ACKed) */
 
diff --git a/net/udp/udp.h b/net/udp/udp.h
index 4d7444a1fe..37737b5eee 100644
--- a/net/udp/udp.h
+++ b/net/udp/udp.h
@@ -34,6 +34,7 @@
 #include <nuttx/semaphore.h>
 #include <nuttx/net/ip.h>
 #include <nuttx/net/net.h>
+#include <nuttx/net/udp.h>
 #include <nuttx/mm/iob.h>
 
 #ifdef CONFIG_NET_UDP_NOTIFIER
@@ -894,7 +895,7 @@ void udp_readahead_signal(FAR struct udp_conn_s *conn);
  *   When write buffer becomes empty, *all* of the workers waiting
  *   for that event data will be executed.  If there are multiple workers
  *   waiting for read-ahead data then only the first to execute will get the
- *   data.  Others will need to call tcp_writebuffer_notifier_setup() once
+ *   data.  Others will need to call udp_writebuffer_notifier_setup() once
  *   again.
  *
  * Input Parameters:
@@ -964,6 +965,22 @@ int udp_ioctl(FAR struct udp_conn_s *conn, int cmd, 
unsigned long arg);
 void udp_sendbuffer_notify(FAR struct udp_conn_s *conn);
 #endif /* CONFIG_NET_SEND_BUFSIZE */
 
+/****************************************************************************
+ * Name: udpip_hdrsize
+ *
+ * Description:
+ *   Get the total size of L3 and L4 UDP header
+ *
+ * Input Parameters:
+ *   conn     The connection structure associated with the socket
+ *
+ * Returned Value:
+ *   the total size of L3 and L4 TCP header
+ *
+ ****************************************************************************/
+
+uint16_t udpip_hdrsize(FAR struct udp_conn_s *conn);
+
 #undef EXTERN
 #ifdef __cplusplus
 }
diff --git a/net/udp/udp_callback.c b/net/udp/udp_callback.c
index a83b96772b..0b13b3f757 100644
--- a/net/udp/udp_callback.c
+++ b/net/udp/udp_callback.c
@@ -70,8 +70,8 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev,
   };
 #endif
 
-  FAR void  *src_addr;
   uint8_t src_addr_size;
+  FAR void  *src_addr;
   uint8_t offset = 0;
 
 #if CONFIG_NET_RECV_BUFSIZE > 0
@@ -82,16 +82,7 @@ static uint16_t udp_datahandler(FAR struct net_driver_s *dev,
     }
 #endif
 
-  /* Allocate on I/O buffer to start the chain (throttling as necessary).
-   * We will not wait for an I/O buffer to become available in this context.
-   */
-
-  iob = iob_tryalloc(true);
-  if (iob == NULL)
-    {
-      nerr("ERROR: Failed to create new I/O buffer chain\n");
-      return 0;
-    }
+  iob = dev->d_iob;
 
 #ifdef CONFIG_NET_IPv6
 #ifdef CONFIG_NET_IPv4
@@ -157,71 +148,18 @@ static uint16_t udp_datahandler(FAR struct net_driver_s 
*dev,
     }
 #endif /* CONFIG_NET_IPv4 */
 
-  /* Copy the src address info into the I/O buffer chain.  We will not wait
-   * for an I/O buffer to become available in this context.  It there is
-   * any failure to allocated, the entire I/O buffer chain will be discarded.
-   */
-
-  ret = iob_trycopyin(iob, (FAR const uint8_t *)&src_addr_size,
-                      sizeof(uint8_t), offset, true);
-  offset += sizeof(uint8_t);
-  if (ret < 0)
-    {
-      /* On a failure, iob_trycopyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
-      iob_free_chain(iob);
-      return 0;
-    }
-
-  ret = iob_trycopyin(iob, (FAR const uint8_t *)src_addr, src_addr_size,
-                      offset, true);
-  offset += src_addr_size;
-  if (ret < 0)
-    {
-      /* On a failure, iob_trycopyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n", ret);
-      iob_free_chain(iob);
-      return 0;
-    }
+  /* Override the address info begin of io_data */
 
 #ifdef CONFIG_NETDEV_IFINDEX
-  ret = iob_trycopyin(iob, &dev->d_ifindex, sizeof(uint8_t), offset, true);
-  offset += sizeof(uint8_t);
-  if (ret < 0)
-    {
-      /* On a failure, iob_trycopyin return a negated error value but does
-       * not free any I/O buffers.
-       */
-
-      nerr("ERROR: Failed to add dindex to the I/O buffer chain: %d\n", ret);
-      iob_free_chain(iob);
-      return 0;
-    }
+  iob->io_data[offset++] = dev->d_ifindex;
 #endif
+  iob->io_data[offset++] = src_addr_size;
+  memcpy(&iob->io_data[offset], src_addr, src_addr_size);
 
-  if (buflen > 0)
-    {
-      /* Copy the new appdata into the I/O buffer chain */
+  /* Trim l3/l4 offset */
 
-      ret = iob_trycopyin(iob, buffer, buflen, offset, true);
-      if (ret < 0)
-        {
-          /* On a failure, iob_trycopyin return a negated error value but
-           * does not free any I/O buffers.
-           */
-
-          nerr("ERROR: Failed to add data to the I/O buffer chain: %d\n",
-               ret);
-          iob_free_chain(iob);
-          return 0;
-        }
-    }
+  iob = iob_trimhead(iob, (dev->d_appdata - iob->io_data) -
+                          iob->io_offset);
 
   /* Add the new I/O buffer chain to the tail of the read-ahead queue */
 
@@ -229,19 +167,24 @@ static uint16_t udp_datahandler(FAR struct net_driver_s 
*dev,
   if (ret < 0)
     {
       nerr("ERROR: Failed to queue the I/O buffer chain: %d\n", ret);
+
       iob_free_chain(iob);
-      return 0;
+      buflen = 0;
     }
-
 #ifdef CONFIG_NET_UDP_NOTIFIER
-  /* Provided notification(s) that additional UDP read-ahead data is
-   * available.
-   */
+  else
+    {
+      ninfo("Buffered %d bytes\n", buflen);
+
+      /* Provided notification(s) that additional UDP read-ahead data is
+       * available.
+       */
 
-  udp_readahead_signal(conn);
+      udp_readahead_signal(conn);
+    }
 #endif
 
-  ninfo("Buffered %d bytes\n", buflen);
+  netdev_iob_clear(dev);
   return buflen;
 }
 
diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c
index dbd639cad8..972dc43940 100644
--- a/net/udp/udp_recvfrom.c
+++ b/net/udp/udp_recvfrom.c
@@ -126,19 +126,19 @@ out:
  *   Copy the read data from the packet
  *
  * Input Parameters:
- *   dev      The structure of the network driver that generated the event.
+ *   dev      The structure of the network driver that generated the event
  *   pstate   recvfrom state structure
  *
  * Returned Value:
- *   The number of bytes taken from the packet.
+ *   None.
  *
  * Assumptions:
  *   The network is locked.
  *
  ****************************************************************************/
 
-static size_t udp_recvfrom_newdata(FAR struct net_driver_s *dev,
-                                   FAR struct udp_recvfrom_s *pstate)
+static inline size_t udp_recvfrom_newdata(FAR struct net_driver_s *dev,
+                                          FAR struct udp_recvfrom_s *pstate)
 {
   size_t recvlen;
 
@@ -155,50 +155,23 @@ static size_t udp_recvfrom_newdata(FAR struct 
net_driver_s *dev,
 
   /* Copy the new appdata into the user buffer */
 
-  memcpy(pstate->ir_msg->msg_iov->iov_base, dev->d_appdata, recvlen);
-  ninfo("Received %zu bytes (of %" PRIu16 ")\n", recvlen, dev->d_len);
+  recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, dev->d_iob,
+                        recvlen, dev->d_appdata - dev->d_iob->io_data -
+                                 dev->d_iob->io_offset);
 
   /* Update the size of the data read */
 
   pstate->ir_recvlen = recvlen;
-  return recvlen;
-}
-
-/****************************************************************************
- * Name: udp_newdata
- *
- * Description:
- *   Copy the read data from the packet
- *
- * Input Parameters:
- *   dev      The structure of the network driver that generated the event
- *   pstate   recvfrom state structure
- *
- * Returned Value:
- *   None.
- *
- * Assumptions:
- *   The network is locked.
- *
- ****************************************************************************/
-
-static inline void udp_newdata(FAR struct net_driver_s *dev,
-                               FAR struct udp_recvfrom_s *pstate)
-{
-  /* Take as much data from the packet as we can */
-
-  udp_recvfrom_newdata(dev, pstate);
-
-  /* Indicate no data in the buffer */
 
   dev->d_len = 0;
+
+  return recvlen;
 }
 
 static inline void udp_readahead(struct udp_recvfrom_s *pstate)
 {
   FAR struct udp_conn_s *conn = pstate->ir_conn;
   FAR struct iob_s *iob;
-  int recvlen;
 
   /* Check there is any UDP datagram already buffered in a read-ahead
    * buffer.
@@ -208,44 +181,34 @@ static inline void udp_readahead(struct udp_recvfrom_s 
*pstate)
 
   if ((iob = iob_peek_queue(&conn->readahead)) != NULL)
     {
-      FAR struct iob_s *tmp;
-#ifdef CONFIG_NET_IPv6
-      uint8_t srcaddr[sizeof(struct sockaddr_in6)];
-#else
-      uint8_t srcaddr[sizeof(struct sockaddr_in)];
-#endif
+      int recvlen = pstate->ir_msg->msg_iov->iov_len;
       uint8_t src_addr_size;
-      uint8_t offset  = 0;
-      uint8_t ifindex = 0;
+      uint8_t offset = 0;
+      FAR void *srcaddr;
+      uint8_t ifindex;
 
       DEBUGASSERT(iob->io_pktlen > 0);
 
-      /* Transfer that buffered data from the I/O buffer chain into
-       * the user buffer.
-       */
-
-      recvlen = iob_copyout(&src_addr_size, iob, sizeof(uint8_t), offset);
-      offset += sizeof(uint8_t);
-      if (recvlen != sizeof(uint8_t))
-        {
-          goto out;
-        }
-
-      recvlen = iob_copyout(srcaddr, iob, src_addr_size, offset);
-      offset += src_addr_size;
-      if (recvlen != src_addr_size)
-        {
-          goto out;
-        }
+      /* Unflatten saved connection information */
 
 #ifdef CONFIG_NETDEV_IFINDEX
-      recvlen = iob_copyout(&ifindex, iob, sizeof(uint8_t), offset);
-      offset += sizeof(uint8_t);
-      if (recvlen != sizeof(uint8_t))
-        {
-          goto out;
-        }
+      ifindex = iob->io_data[offset++];
+#else
+      ifindex = 0;
 #endif
+      src_addr_size = iob->io_data[offset++];
+      srcaddr = &iob->io_data[offset];
+
+      /* Copy to user */
+
+      recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base,
+                            iob, recvlen, 0);
+
+      /* Update the accumulated size of the data read */
+
+      pstate->ir_recvlen = recvlen;
+
+      ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen);
 
       if (pstate->ir_msg->msg_name)
         {
@@ -257,33 +220,13 @@ static inline void udp_readahead(struct udp_recvfrom_s 
*pstate)
                  pstate->ir_msg->msg_namelen);
         }
 
-      if (pstate->ir_msg->msg_iov->iov_len > 0)
-        {
-          recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base,
-                                iob, pstate->ir_msg->msg_iov->iov_len,
-                                offset);
-
-          ninfo("Received %d bytes (of %d)\n", recvlen, iob->io_pktlen);
-
-          /* Update the accumulated size of the data read */
-
-          pstate->ir_recvlen = recvlen;
-        }
-      else
-        {
-          pstate->ir_recvlen = 0;
-        }
-
       udp_recvpktinfo(pstate, srcaddr, ifindex);
 
-out:
       /* Remove the I/O buffer chain from the head of the read-ahead
        * buffer queue.
        */
 
-      tmp = iob_remove_queue(&conn->readahead);
-      DEBUGASSERT(tmp == iob);
-      UNUSED(tmp);
+      iob_remove_queue(&conn->readahead);
 
       /* And free the I/O buffer chain */
 
@@ -481,18 +424,18 @@ static uint16_t udp_eventhandler(FAR struct net_driver_s 
*dev,
 
       else if ((flags & UDP_NEWDATA) != 0)
         {
+          /* Save the sender's address in the caller's 'from' location */
+
+          udp_sender(dev, pstate);
+
           /* Copy the data from the packet */
 
-          udp_newdata(dev, pstate);
+          udp_recvfrom_newdata(dev, pstate);
 
           /* We are finished. */
 
           ninfo("UDP done\n");
 
-          /* Save the sender's address in the caller's 'from' location */
-
-          udp_sender(dev, pstate);
-
           /* Don't allow any further UDP call backs. */
 
           udp_terminate(pstate, OK);
@@ -500,6 +443,12 @@ static uint16_t udp_eventhandler(FAR struct net_driver_s 
*dev,
           /* Indicate that the data has been consumed */
 
           flags &= ~UDP_NEWDATA;
+
+          /* Indicate no data in the buffer */
+
+          netdev_iob_release(dev);
+
+          dev->d_buf = NULL;
         }
     }
 
diff --git a/net/udp/udp_send.c b/net/udp/udp_send.c
index 7298e1e174..7c08b89ec9 100644
--- a/net/udp/udp_send.c
+++ b/net/udp/udp_send.c
@@ -172,6 +172,10 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct 
udp_conn_s *conn)
       udp->udplen      = HTONS(dev->d_sndlen + UDP_HDRLEN);
       udp->udpchksum   = 0;
 
+      /* Update the device buffer length */
+
+      iob_update_pktlen(dev->d_iob, dev->d_len);
+
 #ifdef CONFIG_NET_UDP_CHECKSUMS
       /* Calculate UDP checksum. */
 
@@ -208,4 +212,46 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct 
udp_conn_s *conn)
 #endif
     }
 }
+
+/****************************************************************************
+ * Name: udpip_hdrsize
+ *
+ * Description:
+ *   Get the total size of L3 and L4 UDP header
+ *
+ * Input Parameters:
+ *   conn     The connection structure associated with the socket
+ *
+ * Returned Value:
+ *   the total size of L3 and L4 TCP header
+ *
+ ****************************************************************************/
+
+uint16_t udpip_hdrsize(FAR struct udp_conn_s *conn)
+{
+  uint16_t hdrsize = sizeof(struct udp_hdr_s);
+
+#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
+  /* Which domain the socket used */
+
+  if (conn->domain == PF_INET)
+    {
+      /* Select the IPv4 domain */
+
+      return sizeof(struct ipv4_hdr_s) + hdrsize;
+    }
+  else /* if (domain == PF_INET6) */
+    {
+      /* Select the IPv6 domain */
+
+      return sizeof(struct ipv6_hdr_s) + hdrsize;
+    }
+#elif defined(CONFIG_NET_IPv4)
+  ((void)conn);
+  return sizeof(struct ipv4_hdr_s) + hdrsize;
+#elif defined(CONFIG_NET_IPv6)
+  ((void)conn);
+  return sizeof(struct ipv6_hdr_s) + hdrsize;
+#endif
+}
 #endif /* CONFIG_NET && CONFIG_NET_UDP */
diff --git a/net/udp/udp_sendto_buffered.c b/net/udp/udp_sendto_buffered.c
index 0f48147b50..1fed274f79 100644
--- a/net/udp/udp_sendto_buffered.c
+++ b/net/udp/udp_sendto_buffered.c
@@ -152,6 +152,12 @@ static void sendto_writebuffer_release(FAR struct 
udp_conn_s *conn)
           wrb = (FAR struct udp_wrbuffer_s *)sq_remfirst(&conn->write_q);
           DEBUGASSERT(wrb != NULL);
 
+          /* Do not need to release wb_iob, the life cycle of wb_iob is
+           * handed over to the network device
+           */
+
+          wrb->wb_iob = NULL;
+
           udp_wrbuffer_release(wrb);
 
           /* Set up for the next packet transfer by setting the connection
@@ -399,6 +405,7 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s 
*dev,
   if (dev->d_sndlen <= 0 && (flags & UDP_NEWDATA) == 0 &&
       (flags & UDP_POLL) != 0 && !sq_empty(&conn->write_q))
     {
+      uint16_t udpiplen = udpip_hdrsize(conn);
       FAR struct udp_wrbuffer_s *wrb;
       size_t sndlen;
 
@@ -424,7 +431,7 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s 
*dev,
        * window size.
        */
 
-      sndlen = wrb->wb_iob->io_pktlen;
+      sndlen = wrb->wb_iob->io_pktlen - udpiplen;
       ninfo("wrb=%p sndlen=%zu\n", wrb, sndlen);
 
 #ifdef NEED_IPDOMAIN_SUPPORT
@@ -436,11 +443,16 @@ static uint16_t sendto_eventhandler(FAR struct 
net_driver_s *dev,
 
       sendto_ipselect(dev, conn);
 #endif
+
+      /* Release current device buffer and bypass the iob to l2 driver */
+
+      netdev_iob_release(dev);
+
       /* Then set-up to send that amount of data with the offset
        * corresponding to the size of the IP-dependent address structure.
        */
 
-      devif_iob_send(dev, wrb->wb_iob, sndlen, 0);
+      devif_iob_send(dev, wrb->wb_iob, sndlen, 0, udpiplen);
 
       /* Free the write buffer at the head of the queue and attempt to
        * setup the next transfer.
@@ -496,6 +508,7 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR 
const void *buf,
   FAR struct udp_wrbuffer_s *wrb;
   FAR struct udp_conn_s *conn;
   unsigned int timeout;
+  uint16_t udpiplen;
   bool nonblock;
   bool empty;
   int ret = OK;
@@ -737,6 +750,13 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR 
const void *buf,
           memcpy(&wrb->wb_dest, to, tolen);
         }
 
+      /* Skip l2/l3/l4 offset before copy */
+
+      udpiplen = udpip_hdrsize(conn);
+
+      iob_reserve(wrb->wb_iob, CONFIG_NET_LL_GUARDSIZE);
+      iob_update_pktlen(wrb->wb_iob, udpiplen);
+
       /* Copy the user data into the write buffer.  We cannot wait for
        * buffer space if the socket was opened non-blocking.
        */
@@ -744,7 +764,7 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR 
const void *buf,
       if (nonblock)
         {
           ret = iob_trycopyin(wrb->wb_iob, (FAR uint8_t *)buf,
-                              len, 0, false);
+                              len, udpiplen, false);
         }
       else
         {
@@ -757,7 +777,8 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR 
const void *buf,
            */
 
           blresult = net_breaklock(&count);
-          ret = iob_copyin(wrb->wb_iob, (FAR uint8_t *)buf, len, 0, false);
+          ret = iob_copyin(wrb->wb_iob, (FAR uint8_t *)buf,
+                           len, udpiplen, false);
           if (blresult >= 0)
             {
               net_restorelock(count);
diff --git a/net/udp/udp_sendto_unbuffered.c b/net/udp/udp_sendto_unbuffered.c
index b4bf0da969..0ab154189e 100644
--- a/net/udp/udp_sendto_unbuffered.c
+++ b/net/udp/udp_sendto_unbuffered.c
@@ -62,9 +62,7 @@
 
 struct sendto_s
 {
-#ifdef NEED_IPDOMAIN_SUPPORT
   FAR struct udp_conn_s *st_conn;     /* The UDP connection of interest */
-#endif
   FAR struct devif_callback_s *st_cb; /* Reference to callback instance */
   FAR struct net_driver_s *st_dev;    /* Driver that will perform the 
transmission */
   sem_t st_sem;                       /* Semaphore signals sendto completion */
@@ -206,7 +204,8 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s 
*dev,
 
           /* Copy the user data into d_appdata and send it */
 
-          devif_send(dev, pstate->st_buffer, pstate->st_buflen);
+          devif_send(dev, pstate->st_buffer,
+                     pstate->st_buflen, udpip_hdrsize(pstate->st_conn));
           pstate->st_sndlen = pstate->st_buflen;
         }
 
@@ -400,13 +399,11 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR 
const void *buf,
   state.st_buflen = len;
   state.st_buffer = buf;
 
-#ifdef NEED_IPDOMAIN_SUPPORT
   /* Save the reference to the conn structure if it will be needed for
    * asynchronous processing.
    */
 
   state.st_conn   = conn;
-#endif
 
   /* Check if the socket is connected */
 
diff --git a/net/utils/net_chksum.c b/net/utils/net_chksum.c
index 311dc7e665..fb4933a001 100644
--- a/net/utils/net_chksum.c
+++ b/net/utils/net_chksum.c
@@ -90,6 +90,51 @@ uint16_t chksum(uint16_t sum, FAR const uint8_t *data, 
uint16_t len)
 }
 #endif /* CONFIG_NET_ARCH_CHKSUM */
 
+/****************************************************************************
+ * Name: chksum_iob
+ *
+ * Description:
+ *   Calculate the Internet checksum over an iob chain buffer.
+ *
+ * Input Parameters:
+ *   sum    - Partial calculations carried over from a previous call to
+ *            chksum().  This should be zero on the first time that check
+ *            sum is called.
+ *   iob    - An iob chain buffer over which the checksum is to be computed.
+ *   offset - Specifies the byte offset of the start of valid data.
+ *
+ * Returned Value:
+ *   The updated checksum value.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MM_IOB
+uint16_t chksum_iob(uint16_t sum, FAR struct iob_s *iob, uint16_t offset)
+{
+  /* Skip to the I/O buffer containing the data offset */
+
+  while (iob != NULL && offset > iob->io_len)
+    {
+      offset -= iob->io_len;
+      iob     = iob->io_flink;
+    }
+
+  /* If the link pointer is not empty, loop to walk through all I/O buffer
+   * and accumulate the sum
+   */
+
+  while (iob != NULL)
+    {
+      sum = chksum(sum, iob->io_data + iob->io_offset + offset,
+                   iob->io_len - offset);
+      iob = iob->io_flink;
+      offset = 0;
+    }
+
+  return sum;
+}
+#endif /* CONFIG_MM_IOB */
+
 /****************************************************************************
  * Name: net_chksum
  *
@@ -123,6 +168,39 @@ uint16_t net_chksum(FAR uint16_t *data, uint16_t len)
 }
 #endif /* CONFIG_NET_ARCH_CHKSUM */
 
+/****************************************************************************
+ * Name: net_chksum_iob
+ *
+ * Description:
+ *   Calculate the Internet checksum over an iob chain buffer.
+ *
+ *   The Internet checksum is the one's complement of the one's complement
+ *   sum of all 16-bit words in the buffer.
+ *
+ *   See RFC1071.
+ *
+ *   If CONFIG_NET_ARCH_CHKSUM is defined, then this function must be
+ *   provided by architecture-specific logic.
+ *
+ * Input Parameters:
+ *   sum    - Partial calculations carried over from a previous call to
+ *            chksum().  This should be zero on the first time that check
+ *            sum is called.
+ *   iob    - An iob chain buffer over which the checksum is to be computed.
+ *   offset - Specifies the byte offset of the start of valid data.
+ *
+ * Returned Value:
+ *   The Internet checksum of the given iob chain buffer.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_MM_IOB
+uint16_t net_chksum_iob(uint16_t sum, FAR struct iob_s *iob, uint16_t offset)
+{
+  return HTONS(chksum_iob(sum, iob, offset));
+}
+#endif /* CONFIG_MM_IOB */
+
 /****************************************************************************
  * Name: net_chksum_adjust
  *
diff --git a/net/utils/net_icmpchksum.c b/net/utils/net_icmpchksum.c
index 8a8d42c781..9645e30dc8 100644
--- a/net/utils/net_icmpchksum.c
+++ b/net/utils/net_icmpchksum.c
@@ -61,6 +61,22 @@ uint16_t icmp_chksum(FAR struct net_driver_s *dev, int len)
 }
 #endif /* CONFIG_NET_ICMP */
 
+/****************************************************************************
+ * Name: icmp_chksum_iob
+ *
+ * Description:
+ *   Calculate the checksum of the ICMP message, the input can be an I/O
+ *   buffer chain
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_ICMP) && defined(CONFIG_MM_IOB)
+uint16_t icmp_chksum_iob(FAR struct iob_s *iob)
+{
+  return net_chksum_iob(0, iob, 0);
+}
+#endif /* CONFIG_NET_ICMP */
+
 /****************************************************************************
  * Name: icmpv6_chksum
  *
diff --git a/net/utils/net_ipchksum.c b/net/utils/net_ipchksum.c
index 1da647f719..e0d90bff1a 100644
--- a/net/utils/net_ipchksum.c
+++ b/net/utils/net_ipchksum.c
@@ -93,7 +93,17 @@ uint16_t ipv4_upperlayer_chksum(FAR struct net_driver_s 
*dev, uint8_t proto)
 
   /* Sum IP payload data. */
 
-  sum = chksum(sum, IPBUF(iphdrlen), upperlen);
+#ifdef CONFIG_MM_IOB
+  if (dev->d_iob != NULL)
+    {
+      sum = chksum_iob(sum, dev->d_iob, iphdrlen);
+    }
+  else
+#endif
+    {
+      sum = chksum(sum, IPBUF(iphdrlen), upperlen);
+    }
+
   return (sum == 0) ? 0xffff : HTONS(sum);
 }
 #endif /* CONFIG_NET_ARCH_CHKSUM */
@@ -160,7 +170,17 @@ uint16_t ipv6_upperlayer_chksum(FAR struct net_driver_s 
*dev,
 
   /* Sum IP payload data. */
 
-  sum = chksum(sum, IPBUF(iplen), upperlen);
+#ifdef CONFIG_MM_IOB
+  if (dev->d_iob != NULL)
+    {
+      sum = chksum_iob(sum, dev->d_iob, iplen);
+    }
+  else
+#endif
+    {
+      sum = chksum(sum, IPBUF(iplen), upperlen);
+    }
+
   return (sum == 0) ? 0xffff : HTONS(sum);
 }
 #endif /* CONFIG_NET_ARCH_CHKSUM */
diff --git a/net/utils/utils.h b/net/utils/utils.h
index 83291ecaf7..4bf0e343bf 100644
--- a/net/utils/utils.h
+++ b/net/utils/utils.h
@@ -286,6 +286,19 @@ uint16_t udp_ipv6_chksum(FAR struct net_driver_s *dev);
 uint16_t icmp_chksum(FAR struct net_driver_s *dev, int len);
 #endif
 
+/****************************************************************************
+ * Name: icmp_chksum_iob
+ *
+ * Description:
+ *   Calculate the checksum of the ICMP message, the input can be an I/O
+ *   buffer chain
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_NET_ICMP) && defined(CONFIG_MM_IOB)
+uint16_t icmp_chksum_iob(FAR struct iob_s *iob);
+#endif /* CONFIG_NET_ICMP && CONFIG_MM_IOB */
+
 /****************************************************************************
  * Name: icmpv6_chksum
  *

Reply via email to