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 cb161940c2 udp: Add support for SO_TIMESTAMP
cb161940c2 is described below

commit cb161940c22cb6382d40d2b90b88f975ebbd01ac
Author: Petteri Aimonen <[email protected]>
AuthorDate: Fri Nov 17 09:44:49 2023 +0200

    udp: Add support for SO_TIMESTAMP
    
    Adds support for timestamping received UDP packets, either in
    hardware or in kernel. Builds on the existing support of SO_TIMESTAMP
    for SocketCAN.
    
    Implementation uses CLOCK_REALTIME for timestamping to match the
    behavior of Linux. This could be made configurable in future if needed.
---
 include/nuttx/net/netdev.h | 12 +++++++++++
 net/Kconfig                |  4 ++++
 net/devif/ipv4_input.c     |  6 ++++++
 net/devif/ipv6_input.c     |  6 ++++++
 net/inet/inet_sockif.c     | 51 ++++++++++++++++++++++++++++++++++++++++++++++
 net/socket/Kconfig         |  5 +++--
 net/udp/udp.h              |  4 ++++
 net/udp/udp_callback.c     | 18 +++++++++++++++-
 net/udp/udp_recvfrom.c     | 41 ++++++++++++++++++++++++++++++++++++-
 9 files changed, 143 insertions(+), 4 deletions(-)

diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h
index df9ecefc22..0d35828de3 100644
--- a/include/nuttx/net/netdev.h
+++ b/include/nuttx/net/netdev.h
@@ -442,6 +442,18 @@ struct net_driver_s
   struct netdev_statistics_s d_statistics;
 #endif
 
+#if defined(CONFIG_NET_TIMESTAMP)
+  /* Reception timestamp of packet being currently processed.
+   * If CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP is true, the timestamp is provided
+   * by hardware driver. Otherwise it is filled in by kernel when packet
+   * enters ipv4_input or ipv6_input.
+   *
+   * The timestamp is in CLOCK_REALTIME.
+   */
+
+  struct timespec d_rxtime;
+#endif
+
   /* Application callbacks:
    *
    * Network device event handlers are retained in a 'list' and are called
diff --git a/net/Kconfig b/net/Kconfig
index f2babaa273..1eca79c99e 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -23,6 +23,10 @@ config ARCH_HAVE_NETDEV_STATISTICS
        bool
        default n
 
+config ARCH_HAVE_NETDEV_TIMESTAMP
+       bool
+       default n
+
 config NET_WRITE_BUFFERS
        bool
        default n
diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c
index 5b8f9fc7fc..4e7f2eb16f 100644
--- a/net/devif/ipv4_input.c
+++ b/net/devif/ipv4_input.c
@@ -491,6 +491,12 @@ int ipv4_input(FAR struct net_driver_s *dev)
   FAR uint8_t *buf;
   int ret;
 
+  /* Store reception timestamp if enabled and not provided by hardware. */
+
+#if defined(CONFIG_NET_TIMESTAMP) && 
!defined(CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP)
+  clock_gettime(CLOCK_REALTIME, &dev->d_rxtime);
+#endif
+
   if (dev->d_iob != NULL)
     {
       buf = dev->d_buf;
diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c
index b29daea10d..3be7d875a9 100644
--- a/net/devif/ipv6_input.c
+++ b/net/devif/ipv6_input.c
@@ -609,6 +609,12 @@ int ipv6_input(FAR struct net_driver_s *dev)
   FAR uint8_t *buf;
   int ret;
 
+  /* Store reception timestamp if enabled and not provided by hardware. */
+
+#if defined(CONFIG_NET_TIMESTAMP) && 
!defined(CONFIG_ARCH_HAVE_NETDEV_TIMESTAMP)
+  clock_gettime(CLOCK_REALTIME, &dev->d_rxtime);
+#endif
+
   if (dev->d_iob != NULL)
     {
       buf = dev->d_buf;
diff --git a/net/inet/inet_sockif.c b/net/inet/inet_sockif.c
index 8e6f8bd91c..668047ed40 100644
--- a/net/inet/inet_sockif.c
+++ b/net/inet/inet_sockif.c
@@ -747,6 +747,27 @@ static int inet_get_socketlevel_option(FAR struct socket 
*psock, int option,
         }
 #endif
 
+#ifdef CONFIG_NET_TIMESTAMP
+      case SO_TIMESTAMP:
+        {
+          if (*value_len != sizeof(int))
+            {
+              return -EINVAL;
+            }
+
+          if (psock->s_type == SOCK_DGRAM)
+            {
+              FAR struct udp_conn_s *conn = psock->s_conn;
+              *(FAR int *)value = (conn->timestamp != 0);
+            }
+          else
+            {
+              return -ENOPROTOOPT;
+            }
+        }
+        break;
+#endif
+
       default:
         return -ENOPROTOOPT;
     }
@@ -1028,6 +1049,36 @@ static int inet_set_socketlevel_option(FAR struct socket 
*psock, int option,
         break;
 #endif
 
+#ifdef CONFIG_NET_TIMESTAMP
+      case SO_TIMESTAMP: /* Report receive timestamps as cmsg */
+        {
+          if (value_len < sizeof(int))
+            {
+              return -EINVAL;
+            }
+
+          if (psock->s_type == SOCK_DGRAM)
+            {
+              net_lock();
+
+              /* For now the timestamp enable is just boolean.
+               * If SO_TIMESTAMPING support is added in future, it can be
+               * expanded to flags field for rx/tx timestamps.
+               */
+
+              FAR struct udp_conn_s *conn = psock->s_conn;
+              conn->timestamp = (*((FAR int *)value) != 0);
+
+              net_unlock();
+            }
+          else
+            {
+              return -ENOPROTOOPT;
+            }
+        }
+        break;
+  #endif
+
       default:
         return -ENOPROTOOPT;
     }
diff --git a/net/socket/Kconfig b/net/socket/Kconfig
index 6cfd9f6a95..c05b90ad1c 100644
--- a/net/socket/Kconfig
+++ b/net/socket/Kconfig
@@ -74,9 +74,10 @@ config NET_SOLINGER
 config NET_TIMESTAMP
        bool "SO_TIMESTAMP socket option"
        default n
-       depends on NET_CAN
+       depends on NET_CAN || NET_ETHERNET
        ---help---
-               Enable or disable support for the SO_TIMESTAMP socket option. 
Currently only tested & implemented in SocketCAN but should work on all sockets
+               Enable or disable support for the SO_TIMESTAMP socket option.
+               Supported on SocketCAN and Ethernet/UDP.
 
 config NET_BINDTODEVICE
        bool "SO_BINDTODEVICE socket option Bind-to-device support"
diff --git a/net/udp/udp.h b/net/udp/udp.h
index 3832ec5a0a..8b8aa4bfe1 100644
--- a/net/udp/udp.h
+++ b/net/udp/udp.h
@@ -156,6 +156,10 @@ struct udp_conn_s
    */
 
   struct udp_poll_s pollinfo[CONFIG_NET_UDP_NPOLLWAITERS];
+
+#ifdef CONFIG_NET_TIMESTAMP
+  int timestamp; /* Nonzero when SO_TIMESTAMP is enabled */
+#endif
 };
 
 /* This structure supports UDP write buffering.  It is simply a container
diff --git a/net/udp/udp_callback.c b/net/udp/udp_callback.c
index ee35ba100f..ff417b5d18 100644
--- a/net/udp/udp_callback.c
+++ b/net/udp/udp_callback.c
@@ -28,6 +28,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <debug.h>
+#include <sys/time.h>
 
 #include <nuttx/net/netconfig.h>
 #include <nuttx/net/netdev.h>
@@ -153,11 +154,26 @@ static uint16_t udp_datahandler(FAR struct net_driver_s 
*dev,
 #endif /* CONFIG_NET_IPv4 */
 
   /* Copy the meta info into the I/O buffer chain, just before data.
-   * Layout: |datalen|ifindex|src_addr_size|src_addr|data|
+   * Layout: |datalen|ifindex|src_addr_size|src_addr|[timestamp]|data|
    */
 
   offset = (dev->d_appdata - iob->io_data) - iob->io_offset;
 
+#ifdef CONFIG_NET_TIMESTAMP
+  /* Store timestamp while packet is being queued.
+   * This is done unconditionally to avoid race condition when SO_TIMESTAMP
+   * gets enabled after packet is received but before it is read.
+   */
+
+  offset -= sizeof(struct timespec);
+  ret = iob_trycopyin(iob, (FAR const uint8_t *)&dev->d_rxtime,
+                      sizeof(struct timespec), offset, true);
+  if (ret < 0)
+    {
+      goto errout;
+    }
+#endif
+
   offset -= src_addr_size;
   ret = iob_trycopyin(iob, src_addr, src_addr_size, offset, true);
   if (ret < 0)
diff --git a/net/udp/udp_recvfrom.c b/net/udp/udp_recvfrom.c
index a85b299ad2..1d19a535b5 100644
--- a/net/udp/udp_recvfrom.c
+++ b/net/udp/udp_recvfrom.c
@@ -30,6 +30,7 @@
 #include <debug.h>
 #include <assert.h>
 
+#include <sys/time.h>
 #include <nuttx/semaphore.h>
 #include <nuttx/net/net.h>
 #include <nuttx/mm/iob.h>
@@ -63,6 +64,19 @@ struct udp_recvfrom_s
  * Private Functions
  ****************************************************************************/
 
+#ifdef CONFIG_NET_TIMESTAMP
+static void udp_store_cmsg_timestamp(FAR struct udp_recvfrom_s *pstate,
+                                     FAR struct timespec *timestamp)
+{
+  FAR struct msghdr *msg = pstate->ir_msg;
+  struct timeval tv;
+
+  TIMESPEC_TO_TIMEVAL(&tv, timestamp);
+  cmsg_append(msg, SOL_SOCKET, SO_TIMESTAMP,
+              &tv, sizeof(struct timeval));
+}
+#endif
+
 #ifdef CONFIG_NET_SOCKOPTS
 static void udp_recvpktinfo(FAR struct udp_recvfrom_s *pstate,
                             FAR void *srcaddr, uint8_t ifindex)
@@ -178,7 +192,7 @@ static inline void udp_readahead(struct udp_recvfrom_s 
*pstate)
 #endif
 
       /* Unflatten saved connection information
-       * Layout: |datalen|ifindex|src_addr_size|src_addr|data|
+       * Layout: |datalen|ifindex|src_addr_size|src_addr|[timestamp]|data|
        */
 
       recvlen = iob_copyout((FAR uint8_t *)&datalen, iob,
@@ -202,6 +216,22 @@ static inline void udp_readahead(struct udp_recvfrom_s 
*pstate)
       offset += src_addr_size;
       DEBUGASSERT(recvlen == src_addr_size);
 
+#ifdef CONFIG_NET_TIMESTAMP
+      /* Unpack stored timestamp if SO_TIMESTAMP socket option is enabled */
+
+      if (conn->timestamp)
+        {
+          struct timespec timestamp;
+          recvlen = iob_copyout((FAR uint8_t *)&timestamp, iob,
+                                sizeof(struct timespec), offset);
+          DEBUGASSERT(recvlen == sizeof(struct timespec));
+
+          udp_store_cmsg_timestamp(pstate, &timestamp);
+        }
+
+      offset += sizeof(struct timespec);
+#endif
+
       /* Copy to user */
 
       recvlen = iob_copyout(pstate->ir_msg->msg_iov->iov_base, iob,
@@ -434,6 +464,15 @@ static uint16_t udp_eventhandler(FAR struct net_driver_s 
*dev,
 
       else if ((flags & UDP_NEWDATA) != 0)
         {
+          /* Save packet timestamp, if requested */
+
+#ifdef CONFIG_NET_TIMESTAMP
+          if (pstate->ir_conn->timestamp)
+            {
+              udp_store_cmsg_timestamp(pstate, &dev->d_rxtime);
+            }
+#endif
+
           /* Save the sender's address in the caller's 'from' location */
 
           udp_sender(dev, pstate);

Reply via email to