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 <arpa/inet.h>
      20  #include <string.h>
      21  #include <support/check.h>
      22  #include <support/xsocket.h>
      23  #include <support/xunistd.h>
      24  #include <stdbool.h>
      25  #include <socket-constants-time64.h>
      26  
      27  /* AF_INET socket and address used to receive data.  */
      28  static int srv;
      29  static struct sockaddr_in srv_addr;
      30  
      31  static int
      32  do_sendto (const struct sockaddr_in *addr, int payload)
      33  {
      34    int s = xsocket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
      35    xconnect (s, (const struct sockaddr *) addr, sizeof (*addr));
      36  
      37    xsendto (s, &payload, sizeof (payload), 0, (const struct sockaddr *) addr,
      38  	   sizeof (*addr));
      39  
      40    xclose (s);
      41  
      42    return 0;
      43  }
      44  
      45  static void
      46  do_recvmsg_ancillary (bool use_multi_call, struct mmsghdr *mmhdr,
      47  		      void *msgbuf, size_t msgbuflen, int exp_payload)
      48  {
      49    int payload;
      50    struct iovec iov =
      51      {
      52        .iov_base = &payload,
      53        .iov_len = sizeof (payload)
      54      };
      55    mmhdr->msg_hdr.msg_name = NULL;
      56    mmhdr->msg_hdr.msg_iov = &iov;
      57    mmhdr->msg_hdr.msg_iovlen = 1;
      58    mmhdr->msg_hdr.msg_control = msgbuf;
      59    mmhdr->msg_hdr.msg_controllen = msgbuflen;
      60  
      61    int r;
      62    if (use_multi_call)
      63      {
      64        r = recvmmsg (srv, mmhdr, 1, 0, NULL);
      65        if (r >= 0)
      66  	r = mmhdr->msg_len;
      67      }
      68    else
      69      r = recvmsg (srv, &mmhdr->msg_hdr, 0);
      70    TEST_COMPARE (r, sizeof (int));
      71    TEST_COMPARE (payload, exp_payload);
      72  }
      73  
      74  /* Check if recvmsg create the additional 64 bit timestamp if only 32 bit
      75     is enabled for 64 bit recvmsg symbol.  */
      76  static void
      77  do_test_large_buffer (bool mc)
      78  {
      79    struct mmsghdr mmhdr = { 0 };
      80    /* It should be large enough for either timeval/timespec and the
      81       64 time type as well.  */
      82  
      83    union
      84    {
      85      struct cmsghdr cmsghdr;
      86      char msgbuf[512];
      87    } control;
      88  
      89    /* Enable 32 bit timeval precision and check if no 64 bit timeval stamp
      90       is created.  */
      91    {
      92      int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMP_OLD, &(int){1},
      93  			sizeof (int));
      94      TEST_VERIFY_EXIT (r != -1);
      95  
      96      do_sendto (&srv_addr, 42);
      97      do_recvmsg_ancillary (mc, &mmhdr, &control, sizeof control, 42);
      98  
      99      bool found_timestamp = false;
     100      for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
     101  	 cmsg != NULL;
     102  	 cmsg = CMSG_NXTHDR (&mmhdr.msg_hdr, cmsg))
     103      {
     104        if (cmsg->cmsg_level != SOL_SOCKET)
     105  	continue;
     106  
     107        if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMP_NEW)
     108  	found_timestamp = true;
     109        else
     110  	TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMP_NEW);
     111      }
     112  
     113      TEST_COMPARE (found_timestamp, sizeof (time_t) > 4);
     114    }
     115  
     116    /* Same as before, but for timespec.  */
     117    {
     118      int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMPNS_OLD, &(int){1},
     119  			sizeof (int));
     120      TEST_VERIFY_EXIT (r != -1);
     121  
     122      do_sendto (&srv_addr, 42);
     123      do_recvmsg_ancillary (mc, &mmhdr, &control, sizeof control, 42);
     124  
     125      bool found_timestamp = false;
     126      for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
     127  	 cmsg != NULL;
     128  	 cmsg = CMSG_NXTHDR (&mmhdr.msg_hdr, cmsg))
     129      {
     130        if (cmsg->cmsg_level != SOL_SOCKET)
     131  	continue;
     132  
     133        if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMPNS_NEW)
     134  	found_timestamp = true;
     135        else
     136  	TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMPNS_NEW);
     137      }
     138  
     139      TEST_COMPARE (found_timestamp, sizeof (time_t) > 4);
     140    }
     141  }
     142  
     143  /* Check if recvmsg does not create the additional 64 bit timestamp if
     144     only 32 bit timestamp is enabled if the ancillary buffer is not large
     145     enough.  Also checks if MSG_CTRUNC is set iff for 64 bit recvmsg
     146     symbol.  */
     147  static void
     148  do_test_small_buffer (bool mc)
     149  {
     150    struct mmsghdr mmhdr = { 0 };
     151  
     152    /* Enable 32 bit timeval precision and check if no 64 bit timeval stamp
     153       is created.  */
     154    {
     155      int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMP_OLD, &(int){1},
     156  			sizeof (int));
     157      TEST_VERIFY_EXIT (r != -1);
     158  
     159      union
     160      {
     161        struct cmsghdr cmsghdr;
     162        char msgbuf[CMSG_SPACE (sizeof (struct timeval))];
     163      } control;
     164  
     165      do_sendto (&srv_addr, 42);
     166      do_recvmsg_ancillary (mc, &mmhdr, &control, sizeof control, 42);
     167  
     168      bool found_timestamp = false;
     169      for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
     170  	 cmsg != NULL;
     171  	 cmsg = CMSG_NXTHDR (&mmhdr.msg_hdr, cmsg))
     172      {
     173        if (cmsg->cmsg_level != SOL_SOCKET)
     174  	continue;
     175  
     176        if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMP_NEW)
     177  	found_timestamp = true;
     178        else
     179  	TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMP_NEW);
     180      }
     181  
     182      if (sizeof (time_t) > 4)
     183        {
     184  	TEST_VERIFY ((mmhdr.msg_hdr.msg_flags & MSG_CTRUNC));
     185  	TEST_COMPARE (found_timestamp, 0);
     186        }
     187      else
     188        {
     189  	TEST_VERIFY (!(mmhdr.msg_hdr.msg_flags & MSG_CTRUNC));
     190  	TEST_COMPARE (found_timestamp, 0);
     191        }
     192    }
     193  
     194    /* Same as before, but for timespec.  */
     195    {
     196      int r = setsockopt (srv, SOL_SOCKET, COMPAT_SO_TIMESTAMPNS_OLD, &(int){1},
     197  			sizeof (int));
     198      TEST_VERIFY_EXIT (r != -1);
     199  
     200      union
     201      {
     202        struct cmsghdr cmsghdr;
     203        char msgbuf[CMSG_SPACE (sizeof (struct timespec))];
     204      } control;
     205  
     206      do_sendto (&srv_addr, 42);
     207      do_recvmsg_ancillary (mc, &mmhdr, &control, sizeof control, 42);
     208  
     209      bool found_timestamp = false;
     210      for (struct cmsghdr *cmsg = CMSG_FIRSTHDR (&mmhdr.msg_hdr);
     211  	 cmsg != NULL;
     212  	 cmsg = CMSG_NXTHDR (&mmhdr.msg_hdr, cmsg))
     213      {
     214        if (cmsg->cmsg_level != SOL_SOCKET)
     215  	continue;
     216  
     217        if (sizeof (time_t) > 4 && cmsg->cmsg_type == COMPAT_SO_TIMESTAMPNS_NEW)
     218  	found_timestamp = true;
     219        else
     220  	TEST_VERIFY (cmsg->cmsg_type != COMPAT_SO_TIMESTAMPNS_NEW);
     221      }
     222  
     223      if (sizeof (time_t) > 4)
     224        {
     225  	TEST_VERIFY ((mmhdr.msg_hdr.msg_flags & MSG_CTRUNC));
     226  	TEST_COMPARE (found_timestamp, 0);
     227        }
     228      else
     229        {
     230  	TEST_VERIFY ((mmhdr.msg_hdr.msg_flags & MSG_CTRUNC) == 0);
     231  	TEST_COMPARE (found_timestamp, 0);
     232        }
     233    }
     234  }
     235  
     236  static int
     237  do_test (void)
     238  {
     239    /* This test only make sense for ABIs that support 32 bit time_t socket
     240       timestampss.  */
     241    if (sizeof (time_t) > 4 && __TIMESIZE == 64)
     242      return 0;
     243  
     244    srv = xsocket (AF_INET, SOCK_DGRAM, 0);
     245    srv_addr = (struct sockaddr_in) {
     246      .sin_family = AF_INET,
     247      .sin_addr = {.s_addr = htonl (INADDR_LOOPBACK) },
     248    };
     249    xbind (srv, (struct sockaddr *) &srv_addr, sizeof (srv_addr));
     250    {
     251      socklen_t sa_len = sizeof (srv_addr);
     252      xgetsockname (srv, (struct sockaddr *) &srv_addr, &sa_len);
     253      TEST_VERIFY (sa_len == sizeof (srv_addr));
     254    }
     255  
     256    /* Check recvmsg;  */
     257    do_test_large_buffer (false);
     258    do_test_small_buffer (false);
     259    /* Check recvmmsg.  */
     260    do_test_large_buffer (true);
     261    do_test_small_buffer (true);
     262  
     263    return 0;
     264  }
     265  
     266  #include <support/test-driver.c>