1  /* Check recvmsg/recvmmsg 64-bit timestamp support.
       2     Copyright (C) 2022-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <array_length.h>
      20  #include <arpa/inet.h>
      21  #include <errno.h>
      22  #include <string.h>
      23  #include <stdio.h>
      24  #include <support/check.h>
      25  #include <support/next_to_fault.h>
      26  #include <support/support.h>
      27  #include <support/test-driver.h>
      28  #include <support/xunistd.h>
      29  #include <support/xsocket.h>
      30  #include <sys/mman.h>
      31  
      32  /* Some extra space added for ancillary data, it might be used to convert
      33     32-bit timestamp to 64-bit for _TIME_BITS=64.  */
      34  enum { slack_max_size = 64 };
      35  static const int slack[] = { 0, 4, 8, 16, 32, slack_max_size };
      36  
      37  static bool support_64_timestamp;
      38  /* AF_INET socket and address used to receive data.  */
      39  static int srv;
      40  static struct sockaddr_in srv_addr;
      41  
      42  static int
      43  do_sendto (const struct sockaddr_in *addr, int nmsgs)
      44  {
      45    int s = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
      46    xconnect (s, (const struct sockaddr *) addr, sizeof (*addr));
      47  
      48    for (int i = 0; i < nmsgs; i++)
      49      xsendto (s, &i, sizeof (i), 0, (const struct sockaddr *) addr,
      50  	     sizeof (*addr));
      51  
      52    xclose (s);
      53  
      54    return 0;
      55  }
      56  
      57  static void
      58  do_recvmsg_slack_ancillary (bool use_multi_call, int s, void *cmsg,
      59  			    size_t slack, size_t tsize, int exp_payload)
      60  {
      61    int payload;
      62    struct iovec iov =
      63      {
      64        .iov_base = &payload,
      65        .iov_len = sizeof (payload)
      66      };
      67    size_t msg_controllen = CMSG_SPACE (tsize) + slack;
      68    char *msg_control = cmsg - msg_controllen;
      69    memset (msg_control, 0x55, msg_controllen);
      70    struct mmsghdr mmhdr =
      71      {
      72        .msg_hdr =
      73        {
      74          .msg_name = NULL,
      75          .msg_namelen = 0,
      76          .msg_iov = &iov,
      77          .msg_iovlen = 1,
      78          .msg_control = msg_control,
      79          .msg_controllen = msg_controllen
      80        },
      81      };
      82  
      83    int r;
      84    if (use_multi_call)
      85      {
      86        r = recvmmsg (s, &mmhdr, 1, 0, NULL);
      87        if (r >= 0)
      88  	r = mmhdr.msg_len;
      89      }
      90    else
      91      r = recvmsg (s, &mmhdr.msg_hdr, 0);
      92    TEST_COMPARE (r, sizeof (int));
      93    TEST_COMPARE (payload, exp_payload);
      94  
      95    if (cmsg == NULL)
      96      return;
      97  
      98    /* A timestamp is expected if 32-bit timestamp are used (support in every
      99       configuration) or if underlying kernel support 64-bit timestamps.
     100       Otherwise recvmsg will need extra space do add the 64-bit timestamp.  */
     101    bool exp_timestamp;
     102    if (sizeof (time_t) == 4 || support_64_timestamp)
     103      exp_timestamp = true;
     104     else
     105      exp_timestamp = slack >= CMSG_SPACE (tsize);
     106  
     107    bool timestamp = false;
     108    for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
     109         cmsg != NULL;
     110         cmsg = CMSG_NXTHDR (&mmhdr.msg_hdr, cmsg))
     111      {
     112        if (cmsg->cmsg_level != SOL_SOCKET)
     113  	continue;
     114        if (cmsg->cmsg_type == SCM_TIMESTAMP
     115  	  && cmsg->cmsg_len == CMSG_LEN (sizeof (struct timeval)))
     116  	{
     117  	  struct timeval tv;
     118  	  memcpy (&tv, CMSG_DATA (cmsg), sizeof (tv));
     119  	  if (test_verbose)
     120  	    printf ("SCM_TIMESTAMP:   {%jd, %jd}\n", (intmax_t)tv.tv_sec,
     121  		    (intmax_t)tv.tv_usec);
     122  	  timestamp = true;
     123  	}
     124        else if (cmsg->cmsg_type == SCM_TIMESTAMPNS
     125  	       && cmsg->cmsg_len == CMSG_LEN (sizeof (struct timespec)))
     126  	{
     127  	  struct timespec ts;
     128  	  memcpy (&ts, CMSG_DATA (cmsg), sizeof (ts));
     129  	  if (test_verbose)
     130  	    printf ("SCM_TIMESTAMPNS: {%jd, %jd}\n", (intmax_t)ts.tv_sec,
     131  		    (intmax_t)ts.tv_nsec);
     132  	  timestamp = true;
     133  	}
     134      }
     135  
     136    TEST_COMPARE (timestamp, exp_timestamp);
     137  }
     138  
     139  /* Check if the extra ancillary space is correctly handled by recvmsg and
     140     recvmmsg with different extra space for the ancillaty buffer.  */
     141  static void
     142  do_test_slack_space (void)
     143  {
     144    /* Setup the ancillary data buffer with an extra page with PROT_NONE to
     145       check the possible timestamp conversion on some systems.  */
     146    struct support_next_to_fault nf =
     147      support_next_to_fault_allocate (slack_max_size);
     148    void *msgbuf = nf.buffer + slack_max_size;
     149  
     150    /* Enable the timestamp using struct timeval precision.  */
     151    {
     152      int r = setsockopt (srv, SOL_SOCKET, SO_TIMESTAMP, &(int){1},
     153  			sizeof (int));
     154      TEST_VERIFY_EXIT (r != -1);
     155    }
     156    /* Check recvmsg.  */
     157    do_sendto (&srv_addr, array_length (slack));
     158    for (int s = 0; s < array_length (slack); s++)
     159      {
     160        memset (nf.buffer, 0x55, nf.length);
     161        do_recvmsg_slack_ancillary (false, srv, msgbuf, slack[s],
     162  				  sizeof (struct timeval), s);
     163      }
     164    /* Check recvmmsg.  */
     165    do_sendto (&srv_addr, array_length (slack));
     166    for (int s = 0; s < array_length (slack); s++)
     167      {
     168        memset (nf.buffer, 0x55, nf.length);
     169        do_recvmsg_slack_ancillary (true, srv, msgbuf, slack[s],
     170  				  sizeof (struct timeval), s);
     171      }
     172  
     173    /* Now enable timestamp using a higher precision, it overwrites the previous
     174       precision.  */
     175    {
     176      int r = setsockopt (srv, SOL_SOCKET, SO_TIMESTAMPNS, &(int){1},
     177  			sizeof (int));
     178      TEST_VERIFY_EXIT (r != -1);
     179    }
     180    /* Check recvmsg.  */
     181    do_sendto (&srv_addr, array_length (slack));
     182    for (int s = 0; s < array_length (slack); s++)
     183      do_recvmsg_slack_ancillary (false, srv, msgbuf, slack[s],
     184  				sizeof (struct timespec), s);
     185    /* Check recvmmsg.  */
     186    do_sendto (&srv_addr, array_length (slack));
     187    for (int s = 0; s < array_length (slack); s++)
     188      do_recvmsg_slack_ancillary (true, srv, msgbuf, slack[s],
     189  				sizeof (struct timespec), s);
     190  
     191    support_next_to_fault_free (&nf);
     192  }
     193  
     194  /* Check if the converted 64-bit timestamp is correctly appended when there
     195     are multiple ancillary messages.  */
     196  static void
     197  do_recvmsg_multiple_ancillary (bool use_multi_call, int s, void *cmsg,
     198  			       size_t cmsgsize, int exp_msg)
     199  {
     200    int msg;
     201    struct iovec iov =
     202      {
     203        .iov_base = &msg,
     204        .iov_len = sizeof (msg)
     205      };
     206    size_t msgs = cmsgsize;
     207    struct mmsghdr mmhdr =
     208      {
     209        .msg_hdr =
     210        {
     211          .msg_name = NULL,
     212          .msg_namelen = 0,
     213          .msg_iov = &iov,
     214          .msg_iovlen = 1,
     215          .msg_controllen = msgs,
     216          .msg_control = cmsg,
     217        },
     218      };
     219  
     220    int r;
     221    if (use_multi_call)
     222      {
     223        r = recvmmsg (s, &mmhdr, 1, 0, NULL);
     224        if (r >= 0)
     225  	r = mmhdr.msg_len;
     226      }
     227    else
     228      r = recvmsg (s, &mmhdr.msg_hdr, 0);
     229    TEST_COMPARE (r, sizeof (int));
     230    TEST_COMPARE (msg, exp_msg);
     231  
     232    if (cmsg == NULL)
     233      return;
     234  
     235    bool timestamp = false;
     236    bool origdstaddr = false;
     237    for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
     238         cmsg != NULL;
     239         cmsg = CMSG_NXTHDR (&mmhdr.msg_hdr, cmsg))
     240      {
     241        if (cmsg->cmsg_level == SOL_IP
     242  	  && cmsg->cmsg_type == IP_ORIGDSTADDR
     243  	  && cmsg->cmsg_len >= CMSG_LEN (sizeof (struct sockaddr_in)))
     244  	{
     245  	  struct sockaddr_in sa;
     246  	  memcpy (&sa, CMSG_DATA (cmsg), sizeof (sa));
     247  	  if (test_verbose)
     248  	    {
     249  	      char str[INET_ADDRSTRLEN];
     250  	      inet_ntop (AF_INET, &sa.sin_addr, str, INET_ADDRSTRLEN);
     251  	      printf ("IP_ORIGDSTADDR:  %s:%d\n", str, ntohs (sa.sin_port));
     252  	    }
     253  	  origdstaddr = sa.sin_addr.s_addr == srv_addr.sin_addr.s_addr
     254  			&& sa.sin_port == srv_addr.sin_port;
     255  	}
     256        if (cmsg->cmsg_level == SOL_SOCKET
     257  	  && cmsg->cmsg_type == SCM_TIMESTAMP
     258  	  && cmsg->cmsg_len >= CMSG_LEN (sizeof (struct timeval)))
     259  	{
     260  	  struct timeval tv;
     261  	  memcpy (&tv, CMSG_DATA (cmsg), sizeof (tv));
     262  	  if (test_verbose)
     263  	    printf ("SCM_TIMESTAMP:   {%jd, %jd}\n", (intmax_t)tv.tv_sec,
     264  		    (intmax_t)tv.tv_usec);
     265  	  timestamp = true;
     266  	}
     267      }
     268  
     269    TEST_COMPARE (timestamp, true);
     270    TEST_COMPARE (origdstaddr, true);
     271  }
     272  
     273  static void
     274  do_test_multiple_ancillary (void)
     275  {
     276    {
     277      int r = setsockopt (srv, SOL_SOCKET, SO_TIMESTAMP, &(int){1},
     278  			sizeof (int));
     279      TEST_VERIFY_EXIT (r != -1);
     280    }
     281    {
     282      int r = setsockopt (srv, IPPROTO_IP, IP_RECVORIGDSTADDR, &(int){1},
     283  			sizeof (int));
     284      TEST_VERIFY_EXIT (r != -1);
     285    }
     286  
     287    /* Enough data for default SO_TIMESTAMP, the IP_RECVORIGDSTADDR, and the
     288       extra 64-bit SO_TIMESTAMP.  */
     289    enum { msgbuflen = CMSG_SPACE (2 * sizeof (uint64_t))
     290  		     + CMSG_SPACE (sizeof (struct sockaddr_in))
     291  		     + CMSG_SPACE (2 * sizeof (uint64_t)) };
     292    char msgbuf[msgbuflen];
     293  
     294    enum { nmsgs = 8 };
     295    /* Check recvmsg.  */
     296    do_sendto (&srv_addr, nmsgs);
     297    for (int s = 0; s < nmsgs; s++)
     298      do_recvmsg_multiple_ancillary (false, srv, msgbuf, msgbuflen, s);
     299    /* Check recvmmsg.  */
     300    do_sendto (&srv_addr, nmsgs);
     301    for (int s = 0; s < nmsgs; s++)
     302      do_recvmsg_multiple_ancillary (true, srv, msgbuf, msgbuflen, s);
     303  }
     304  
     305  static int
     306  do_test (void)
     307  {
     308    srv = xsocket (AF_INET, SOCK_DGRAM, 0);
     309    srv_addr = (struct sockaddr_in) {
     310      .sin_family = AF_INET,
     311      .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK) },
     312    };
     313    xbind (srv, (struct sockaddr *) &srv_addr, sizeof (srv_addr));
     314    {
     315      socklen_t sa_len = sizeof (srv_addr);
     316      xgetsockname (srv, (struct sockaddr *) &srv_addr, &sa_len);
     317      TEST_VERIFY (sa_len == sizeof (srv_addr));
     318    }
     319  
     320    TEST_COMPARE (recvmsg (-1, NULL, 0), -1);
     321    TEST_COMPARE (errno, EBADF);
     322    TEST_COMPARE (recvmmsg (-1, NULL, 0, 0, NULL), -1);
     323    TEST_COMPARE (errno, EBADF);
     324  
     325    /* If underlying kernel does not support   */
     326    support_64_timestamp = support_socket_so_timestamp_time64 (srv);
     327  
     328    do_test_slack_space ();
     329    do_test_multiple_ancillary ();
     330  
     331    xclose (srv);
     332  
     333    return 0;
     334  }
     335  
     336  #include <support/test-driver.c>