diff mbox series

[v2,06/25] linux: Add fallback for 64-bit time_t SO_TIMESTAMP{NS}

Message ID 20210518205613.1487824-7-adhemerval.zanella@linaro.org
State New
Headers show
Series Add 64 bit time support on legacy ABIs | expand

Commit Message

Adhemerval Zanella Netto May 18, 2021, 8:55 p.m. UTC
The recvmsg handling is more complicated because it requires check the
returned kernel control message and make some convertions.  For
!__ASSUME_TIME64_SYSCALLS it converts the first 32-bit time SO_TIMESTAMP
or SO_TIMESTAMPNS and appends it to the control buffer if has extra
space or returns MSG_CTRUNC otherwise.  The 32-bit time field is kept
as-is.

Calls with __TIMESIZE=32 will see the converted 64-bit time control
messages as spurious control message of unknown type.  Calls with
__TIMESIZE=64 running on pre-time64 kernels will see the original
message as a spurious control ones of unknown typ while running on
kernel with native 64-bit time support will only see the time64 version
of the control message.

Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15
kernel).
---
 include/sys/socket.h                          |  5 +
 sysdeps/unix/sysv/linux/Makefile              |  2 +-
 sysdeps/unix/sysv/linux/Versions              |  1 +
 .../unix/sysv/linux/convert_scm_timestamps.c  | 96 +++++++++++++++++++
 sysdeps/unix/sysv/linux/getsockopt.c          | 12 +++
 .../sysv/linux/hppa/socket-constants-time64.h |  5 +
 .../sysv/linux/mips/socket-constants-time64.h |  5 +
 .../linux/powerpc/socket-constants-time64.h   |  5 +
 sysdeps/unix/sysv/linux/recvmsg.c             | 23 +++--
 sysdeps/unix/sysv/linux/setsockopt.c          | 12 +++
 .../unix/sysv/linux/socket-constants-time64.h |  5 +
 .../linux/sparc/socket-constants-time64.h     |  5 +
 12 files changed, 169 insertions(+), 7 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/convert_scm_timestamps.c

Comments

Lukasz Majewski May 19, 2021, 8:50 a.m. UTC | #1
On Tue, 18 May 2021 17:55:54 -0300
Adhemerval Zanella <adhemerval.zanella@linaro.org> wrote:

> The recvmsg handling is more complicated because it requires check the
> returned kernel control message and make some convertions.  For
> !__ASSUME_TIME64_SYSCALLS it converts the first 32-bit time
> SO_TIMESTAMP or SO_TIMESTAMPNS and appends it to the control buffer
> if has extra space or returns MSG_CTRUNC otherwise.  The 32-bit time
> field is kept as-is.
> 
> Calls with __TIMESIZE=32 will see the converted 64-bit time control
> messages as spurious control message of unknown type.  Calls with
> __TIMESIZE=64 running on pre-time64 kernels will see the original
> message as a spurious control ones of unknown typ while running on
> kernel with native 64-bit time support will only see the time64
> version of the control message.
> 

Reviewed-by: Lukasz Majewski <lukma@denx.de>

> Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15
> kernel).
> ---
>  include/sys/socket.h                          |  5 +
>  sysdeps/unix/sysv/linux/Makefile              |  2 +-
>  sysdeps/unix/sysv/linux/Versions              |  1 +
>  .../unix/sysv/linux/convert_scm_timestamps.c  | 96
> +++++++++++++++++++ sysdeps/unix/sysv/linux/getsockopt.c          |
> 12 +++ .../sysv/linux/hppa/socket-constants-time64.h |  5 +
>  .../sysv/linux/mips/socket-constants-time64.h |  5 +
>  .../linux/powerpc/socket-constants-time64.h   |  5 +
>  sysdeps/unix/sysv/linux/recvmsg.c             | 23 +++--
>  sysdeps/unix/sysv/linux/setsockopt.c          | 12 +++
>  .../unix/sysv/linux/socket-constants-time64.h |  5 +
>  .../linux/sparc/socket-constants-time64.h     |  5 +
>  12 files changed, 169 insertions(+), 7 deletions(-)
>  create mode 100644 sysdeps/unix/sysv/linux/convert_scm_timestamps.c
> 
> diff --git a/include/sys/socket.h b/include/sys/socket.h
> index 0e39dd2a3a..15d4a62b26 100644
> --- a/include/sys/socket.h
> +++ b/include/sys/socket.h
> @@ -164,5 +164,10 @@ libc_hidden_proto (__libc_sa_len)
>  
>  libc_hidden_proto (__cmsg_nxthdr)
>  
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +extern void __convert_scm_timestamps (struct msghdr *msg, socklen_t
> msgsize) ; +libc_hidden_proto (__convert_scm_timestamps)
> +#endif
> +
>  #endif
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/Makefile
> b/sysdeps/unix/sysv/linux/Makefile index fb155cf856..e28f6470e3 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -64,7 +64,7 @@ sysdep_routines += adjtimex clone umount umount2
> readahead sysctl \ time64-support pselect32 \
>  		   xstat fxstat lxstat xstat64 fxstat64 lxstat64 \
>  		   fxstatat fxstatat64 \
> -		   xmknod xmknodat
> +		   xmknod xmknodat convert_scm_timestamps
>  
>  CFLAGS-gethostid.c = -fexceptions
>  CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
> diff --git a/sysdeps/unix/sysv/linux/Versions
> b/sysdeps/unix/sysv/linux/Versions index 220bb2dffe..148f04c50a 100644
> --- a/sysdeps/unix/sysv/linux/Versions
> +++ b/sysdeps/unix/sysv/linux/Versions
> @@ -177,6 +177,7 @@ libc {
>      __pread64_nocancel;
>      __close_nocancel;
>      __sigtimedwait;
> +    __convert_scm_timestamps;
>      # functions used by nscd
>      __netlink_assert_response;
>    }
> diff --git a/sysdeps/unix/sysv/linux/convert_scm_timestamps.c
> b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c new file mode
> 100644 index 0000000000..3c123c28ce
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c
> @@ -0,0 +1,96 @@
> +/* Socket timestamp conversion routines.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be
> useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <kernel-features.h>
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +# include <stdint.h>
> +# include <string.h>
> +# include <sys/socket.h>
> +# include <socket-constants-time64.h>
> +
> +/* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit
> time and
> +   appends it to the control buffer.  The 32-bit time field is kept
> as-is. +
> +   Calls with __TIMESIZE=32 will see the converted 64-bit time
> control
> +   messages as spurious control message of unknown type.
> +
> +   Calls with __TIMESIZE=64 running on pre-time64 kernels will see
> the
> +   original message as a spurious control ones of unknown typ while
> running
> +   on kernel with native 64-bit time support will only see the
> time64 version
> +   of the control message.  */
> +void
> +__convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize)
> +{
> +  if (msg->msg_control == NULL || msg->msg_controllen == 0)
> +    return;
> +
> +  /* The returned control message format for SO_TIMESTAMP_NEW is a
> +     'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a
> +     'struct __kernel_timespec'.  In either case it is two uint64_t
> +     members.  */
> +  uint64_t tvts[2];
> +
> +  struct cmsghdr *cmsg, *last = NULL;
> +  int type = 0;
> +
> +  for (cmsg = CMSG_FIRSTHDR (msg);
> +       cmsg != NULL;
> +       cmsg = CMSG_NXTHDR (msg, cmsg))
> +    {
> +      if (cmsg->cmsg_level != SOL_SOCKET)
> +	continue;
> +
> +      switch (cmsg->cmsg_type)
> +	{
> +	case COMPAT_SO_TIMESTAMP_OLD:
> +	  if (type != 0)
> +	    break;
> +	  type = COMPAT_SO_TIMESTAMP_NEW;
> +	  goto common;
> +
> +	case COMPAT_SO_TIMESTAMPNS_OLD:
> +	  type = COMPAT_SO_TIMESTAMPNS_NEW;
> +
> +	/* fallthrough  */
> +	common:
> +	  memcpy (tvts, CMSG_DATA (cmsg), sizeof (tvts));
> +	  break;
> +	}
> +
> +      last = cmsg;
> +    }
> +
> +  if (last == NULL || type == 0)
> +    return;
> +
> +  if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen)
> +    {
> +      msg->msg_flags |= MSG_CTRUNC;
> +      return;
> +    }
> +
> +  msg->msg_controllen += CMSG_SPACE (sizeof tvts);
> +  cmsg = CMSG_NXTHDR(msg, last);
> +  cmsg->cmsg_level = SOL_SOCKET;
> +  cmsg->cmsg_type = type;
> +  cmsg->cmsg_len = CMSG_LEN (sizeof tvts);
> +  memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts);
> +}
> +libc_hidden_def (__convert_scm_timestamps)
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/getsockopt.c
> b/sysdeps/unix/sysv/linux/getsockopt.c index c8e502d976..14b782d0da
> 100644 --- a/sysdeps/unix/sysv/linux/getsockopt.c
> +++ b/sysdeps/unix/sysv/linux/getsockopt.c
> @@ -67,6 +67,18 @@ getsockopt32 (int fd, int level, int optname, void
> *optval, *tv64 = valid_timeval32_to_timeval64 (tv32);
>  	*len = sizeof (*tv64);
>        }
> +      break;
> +
> +    case COMPAT_SO_TIMESTAMP_NEW:
> +    case COMPAT_SO_TIMESTAMPNS_NEW:
> +      {
> +	if (optname == COMPAT_SO_TIMESTAMP_NEW)
> +	  optname = COMPAT_SO_TIMESTAMP_OLD;
> +	if (optname == COMPAT_SO_TIMESTAMPNS_NEW)
> +	  optname = COMPAT_SO_TIMESTAMPNS_OLD;
> +	r = getsockopt_syscall (fd, level, optname, optval, len);
> +      }
> +      break;
>      }
>  
>    return r;
> diff --git a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h index
> ea721e0fc2..f3b98012d5 100644 ---
> a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h +++
> b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h @@ -27,4
> +27,9 @@ #define COMPAT_SO_RCVTIMEO_NEW 16448
>  #define COMPAT_SO_SNDTIMEO_NEW 16449
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 0x4012
> +#define COMPAT_SO_TIMESTAMPNS_OLD 0x4013
> +#define COMPAT_SO_TIMESTAMP_NEW 0x4038
> +#define COMPAT_SO_TIMESTAMPNS_NEW 0x4039
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h index
> ab8bd62853..31fa69fa9f 100644 ---
> a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h +++
> b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h @@ -27,4
> +27,9 @@ #define COMPAT_SO_RCVTIMEO_NEW 66
>  #define COMPAT_SO_SNDTIMEO_NEW 67
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 29
> +#define COMPAT_SO_TIMESTAMPNS_OLD 35
> +#define COMPAT_SO_TIMESTAMP_NEW 63
> +#define COMPAT_SO_TIMESTAMPNS_NEW 64
> +
>  #endif
> diff --git
> a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h index
> 1e48dcca8d..889251895b 100644 ---
> a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h +++
> b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h @@ -27,4
> +27,9 @@ #define COMPAT_SO_RCVTIMEO_NEW 66 #define
> COMPAT_SO_SNDTIMEO_NEW 67 
> +#define COMPAT_SO_TIMESTAMP_OLD 29
> +#define COMPAT_SO_TIMESTAMPNS_OLD 35
> +#define COMPAT_SO_TIMESTAMP_NEW 63
> +#define COMPAT_SO_TIMESTAMPNS_NEW 64
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/recvmsg.c
> b/sysdeps/unix/sysv/linux/recvmsg.c index b209b4ad99..a2a600228b
> 100644 --- a/sysdeps/unix/sysv/linux/recvmsg.c
> +++ b/sysdeps/unix/sysv/linux/recvmsg.c
> @@ -19,16 +19,27 @@
>  #include <sys/socket.h>
>  #include <sysdep-cancel.h>
>  #include <socketcall.h>
> -#include <shlib-compat.h>
>  
>  ssize_t
>  __libc_recvmsg (int fd, struct msghdr *msg, int flags)
>  {
> -# ifdef __ASSUME_RECVMSG_SYSCALL
> -  return SYSCALL_CANCEL (recvmsg, fd, msg, flags);
> -# else
> -  return SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
> -# endif
> +  ssize_t r;
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +  socklen_t orig_controllen = msg->msg_controllen;
> +#endif
> +
> +#ifdef __ASSUME_RECVMSG_SYSCALL
> +  r = SYSCALL_CANCEL (recvmsg, fd, msg, flags);
> +#else
> +  r = SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
> +#endif
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +  if (r >= 0)
> +    __convert_scm_timestamps (msg, orig_controllen);
> +#endif
> +
> +  return r;
>  }
>  weak_alias (__libc_recvmsg, recvmsg)
>  weak_alias (__libc_recvmsg, __recvmsg)
> diff --git a/sysdeps/unix/sysv/linux/setsockopt.c
> b/sysdeps/unix/sysv/linux/setsockopt.c index 6505202265..a4780a9d33
> 100644 --- a/sysdeps/unix/sysv/linux/setsockopt.c
> +++ b/sysdeps/unix/sysv/linux/setsockopt.c
> @@ -69,6 +69,18 @@ setsockopt32 (int fd, int level, int optname,
> const void *optval, 
>  	r = setsockopt_syscall (fd, level, optname, &tv32, sizeof
> (tv32)); }
> +      break;
> +
> +    case COMPAT_SO_TIMESTAMP_NEW:
> +    case COMPAT_SO_TIMESTAMPNS_NEW:
> +      {
> +	if (optname == COMPAT_SO_TIMESTAMP_NEW)
> +	  optname = COMPAT_SO_TIMESTAMP_OLD;
> +	if (optname == COMPAT_SO_TIMESTAMPNS_NEW)
> +	  optname = COMPAT_SO_TIMESTAMPNS_OLD;
> +	r = setsockopt_syscall (fd, level, optname, NULL, 0);
> +      }
> +      break;
>      }
>  
>    return r;
> diff --git a/sysdeps/unix/sysv/linux/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/socket-constants-time64.h index
> e5a3777f28..7f7ca05504 100644 ---
> a/sysdeps/unix/sysv/linux/socket-constants-time64.h +++
> b/sysdeps/unix/sysv/linux/socket-constants-time64.h @@ -27,4 +27,9 @@
>  #define COMPAT_SO_RCVTIMEO_NEW 66
>  #define COMPAT_SO_SNDTIMEO_NEW 67
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 29
> +#define COMPAT_SO_TIMESTAMPNS_OLD 35
> +#define COMPAT_SO_TIMESTAMP_NEW 63
> +#define COMPAT_SO_TIMESTAMPNS_NEW 64
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h index
> b137abdeea..56358923e1 100644 ---
> a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h +++
> b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h @@ -27,4
> +27,9 @@ #define COMPAT_SO_RCVTIMEO_NEW 68
>  #define COMPAT_SO_SNDTIMEO_NEW 69
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 0x001d
> +#define COMPAT_SO_TIMESTAMPNS_OLD 0x0021
> +#define COMPAT_SO_TIMESTAMP_NEW 0x0046
> +#define COMPAT_SO_TIMESTAMPNS_NEW 0x0042
> +
>  #endif



Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
Florian Weimer May 20, 2021, 6:50 a.m. UTC | #2
* Adhemerval Zanella via Libc-alpha:

> Calls with __TIMESIZE=32 will see the converted 64-bit time control
> messages as spurious control message of unknown type.  Calls with
> __TIMESIZE=64 running on pre-time64 kernels will see the original
> message as a spurious control ones of unknown typ while running on
> kernel with native 64-bit time support will only see the time64 version
> of the control message.

Does the mirror what the kernel does?  I have some concerns about
backwards compatibility here, but if the kernel does it as well, that is
likely a non-issue.

I think this kind of emulation goes against our general guidance of not
emulating system calls in userspace.  It would probably be necessary if
we switch an existing 32-bit target to 64-bit time_t by default, though.

Thanks,
Florian
Adhemerval Zanella Netto May 20, 2021, 6:46 p.m. UTC | #3
On 20/05/2021 03:50, Florian Weimer wrote:
> * Adhemerval Zanella via Libc-alpha:
> 
>> Calls with __TIMESIZE=32 will see the converted 64-bit time control
>> messages as spurious control message of unknown type.  Calls with
>> __TIMESIZE=64 running on pre-time64 kernels will see the original
>> message as a spurious control ones of unknown typ while running on
>> kernel with native 64-bit time support will only see the time64 version
>> of the control message.
> 
> Does the mirror what the kernel does?  I have some concerns about
> backwards compatibility here, but if the kernel does it as well, that is
> likely a non-issue.

The SO_TIMESTAMP{NS}_OLD to SO_TIMESTAMP{NS}_NEW for _TIME_BITS=64 is
what this patch does to add some compatibility. From kernel code:
net/socket.c

 772 void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
 773         struct sk_buff *skb)
 774 {
 775         int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP);
 776         int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW);

[...]
 790         if (need_software_tstamp) {
 791                 if (!sock_flag(sk, SOCK_RCVTSTAMPNS)) {
 792                         if (new_tstamp) {
 793                                 struct __kernel_sock_timeval tv;
 794 
 795                                 skb_get_new_timestamp(skb, &tv);
 796                                 put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_NEW,
 797                                          sizeof(tv), &tv);
 798                         } else {
 799                                 struct __kernel_old_timeval tv;
 800 
 801                                 skb_get_timestamp(skb, &tv);
 802                                 put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMP_OLD,
 803                                          sizeof(tv), &tv);
 804                         }
 805                 } else {
 806                         if (new_tstamp) {
 807                                 struct __kernel_timespec ts;
 808 
 809                                 skb_get_new_timestampns(skb, &ts);
 810                                 put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_NEW,
 811                                          sizeof(ts), &ts);
 812                         } else {
 813                                 struct __kernel_old_timespec ts;
 814 
 815                                 skb_get_timestampns(skb, &ts);
 816                                 put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPNS_OLD,
 817                                          sizeof(ts), &ts);
 818                         }
 819                 }
 820         }

For _TIME_BITS=32, SO_TIMESTAMP{NS} will be exported as the 
SO_TIMESTAMP{NS}_OLD and if the process sets a SO_TIMESTAMPING{NS}_NEW 
somehow the control message will be unknown.

For _TIME_BITS=64, SO_TIMESTAMP will be exported as the SO_TIMESTAMP_NEW.
Programs that set SO_TIMESTAMP{NS}_OLD will be converted to 
SO_TIMESTAMP{NS}_NEW.  This translation is *not* done by the kernel,
but the conversion should be safe as long CMSG_SPACE is large enough (and
the __convert_scm_timestamps does check for it).

> 
> I think this kind of emulation goes against our general guidance of not
> emulating system calls in userspace.  It would probably be necessary if
> we switch an existing 32-bit target to 64-bit time_t by default, though.

I am not sure this characterize as emulation, but rather fall in what we
do for 64 time support. Otherwise newer code won't see SO_TIMESTAMP{NS}_OLD
values and I think it might be a more difficult breakage than getting
32-bit timestamp values.
Florian Weimer May 21, 2021, 6:38 p.m. UTC | #4
* Adhemerval Zanella:

> On 20/05/2021 03:50, Florian Weimer wrote:
>> * Adhemerval Zanella via Libc-alpha:
>> 
>>> Calls with __TIMESIZE=32 will see the converted 64-bit time control
>>> messages as spurious control message of unknown type.  Calls with
>>> __TIMESIZE=64 running on pre-time64 kernels will see the original
>>> message as a spurious control ones of unknown typ while running on
>>> kernel with native 64-bit time support will only see the time64 version
>>> of the control message.
>> 
>> Does the mirror what the kernel does?  I have some concerns about
>> backwards compatibility here, but if the kernel does it as well, that is
>> likely a non-issue.
>
> The SO_TIMESTAMP{NS}_OLD to SO_TIMESTAMP{NS}_NEW for _TIME_BITS=64 is
> what this patch does to add some compatibility. From kernel code:
> net/socket.c

Will anything new show up for _TIME_BITS=32 on legacy architectures?

Thanks,
Florian
Adhemerval Zanella Netto May 21, 2021, 7:02 p.m. UTC | #5
On 21/05/2021 15:38, Florian Weimer wrote:
> * Adhemerval Zanella:
> 
>> On 20/05/2021 03:50, Florian Weimer wrote:
>>> * Adhemerval Zanella via Libc-alpha:
>>>
>>>> Calls with __TIMESIZE=32 will see the converted 64-bit time control
>>>> messages as spurious control message of unknown type.  Calls with
>>>> __TIMESIZE=64 running on pre-time64 kernels will see the original
>>>> message as a spurious control ones of unknown typ while running on
>>>> kernel with native 64-bit time support will only see the time64 version
>>>> of the control message.
>>>
>>> Does the mirror what the kernel does?  I have some concerns about
>>> backwards compatibility here, but if the kernel does it as well, that is
>>> likely a non-issue.
>>
>> The SO_TIMESTAMP{NS}_OLD to SO_TIMESTAMP{NS}_NEW for _TIME_BITS=64 is
>> what this patch does to add some compatibility. From kernel code:
>> net/socket.c
> 
> Will anything new show up for _TIME_BITS=32 on legacy architectures?

It will if a library setup a SO_TIMESTAMP{NS}_NEW (assuming running on
newer kernels), since afaik the timestamp is set on socket buffer within 
kernel (so my understanding is in theory one can see multiple different 
timestamps formats on same socket).
Carlos O'Donell June 4, 2021, 7:30 p.m. UTC | #6
On 5/18/21 4:55 PM, Adhemerval Zanella wrote:
> The recvmsg handling is more complicated because it requires check the
> returned kernel control message and make some convertions.  For
> !__ASSUME_TIME64_SYSCALLS it converts the first 32-bit time SO_TIMESTAMP
> or SO_TIMESTAMPNS and appends it to the control buffer if has extra
> space or returns MSG_CTRUNC otherwise.  The 32-bit time field is kept
> as-is.
> 
> Calls with __TIMESIZE=32 will see the converted 64-bit time control
> messages as spurious control message of unknown type.  Calls with
> __TIMESIZE=64 running on pre-time64 kernels will see the original
> message as a spurious control ones of unknown typ while running on
> kernel with native 64-bit time support will only see the time64 version
> of the control message.
> 
> Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15
> kernel).

LGTM.

No regressions on x86_64, i686, ppc64le, aarch64, s390x.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Tested-by: Carlos O'Donell <carlos@redhat.com>

> ---
>  include/sys/socket.h                          |  5 +
>  sysdeps/unix/sysv/linux/Makefile              |  2 +-
>  sysdeps/unix/sysv/linux/Versions              |  1 +
>  .../unix/sysv/linux/convert_scm_timestamps.c  | 96 +++++++++++++++++++
>  sysdeps/unix/sysv/linux/getsockopt.c          | 12 +++
>  .../sysv/linux/hppa/socket-constants-time64.h |  5 +
>  .../sysv/linux/mips/socket-constants-time64.h |  5 +
>  .../linux/powerpc/socket-constants-time64.h   |  5 +
>  sysdeps/unix/sysv/linux/recvmsg.c             | 23 +++--
>  sysdeps/unix/sysv/linux/setsockopt.c          | 12 +++
>  .../unix/sysv/linux/socket-constants-time64.h |  5 +
>  .../linux/sparc/socket-constants-time64.h     |  5 +
>  12 files changed, 169 insertions(+), 7 deletions(-)
>  create mode 100644 sysdeps/unix/sysv/linux/convert_scm_timestamps.c
> 
> diff --git a/include/sys/socket.h b/include/sys/socket.h
> index 0e39dd2a3a..15d4a62b26 100644
> --- a/include/sys/socket.h
> +++ b/include/sys/socket.h
> @@ -164,5 +164,10 @@ libc_hidden_proto (__libc_sa_len)
>  
>  libc_hidden_proto (__cmsg_nxthdr)
>  
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +extern void __convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize) ;
> +libc_hidden_proto (__convert_scm_timestamps)
> +#endif
> +
>  #endif
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
> index fb155cf856..e28f6470e3 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -64,7 +64,7 @@ sysdep_routines += adjtimex clone umount umount2 readahead sysctl \
>  		   time64-support pselect32 \
>  		   xstat fxstat lxstat xstat64 fxstat64 lxstat64 \
>  		   fxstatat fxstatat64 \
> -		   xmknod xmknodat
> +		   xmknod xmknodat convert_scm_timestamps
>  
>  CFLAGS-gethostid.c = -fexceptions
>  CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
> diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
> index 220bb2dffe..148f04c50a 100644
> --- a/sysdeps/unix/sysv/linux/Versions
> +++ b/sysdeps/unix/sysv/linux/Versions
> @@ -177,6 +177,7 @@ libc {
>      __pread64_nocancel;
>      __close_nocancel;
>      __sigtimedwait;
> +    __convert_scm_timestamps;
>      # functions used by nscd
>      __netlink_assert_response;
>    }
> diff --git a/sysdeps/unix/sysv/linux/convert_scm_timestamps.c b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c
> new file mode 100644
> index 0000000000..3c123c28ce
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c
> @@ -0,0 +1,96 @@
> +/* Socket timestamp conversion routines.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <kernel-features.h>
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +# include <stdint.h>
> +# include <string.h>
> +# include <sys/socket.h>
> +# include <socket-constants-time64.h>
> +
> +/* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and
> +   appends it to the control buffer.  The 32-bit time field is kept as-is.
> +
> +   Calls with __TIMESIZE=32 will see the converted 64-bit time control
> +   messages as spurious control message of unknown type.
> +
> +   Calls with __TIMESIZE=64 running on pre-time64 kernels will see the
> +   original message as a spurious control ones of unknown typ while running
> +   on kernel with native 64-bit time support will only see the time64 version
> +   of the control message.  */
> +void
> +__convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize)
> +{
> +  if (msg->msg_control == NULL || msg->msg_controllen == 0)
> +    return;
> +
> +  /* The returned control message format for SO_TIMESTAMP_NEW is a
> +     'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a
> +     'struct __kernel_timespec'.  In either case it is two uint64_t
> +     members.  */
> +  uint64_t tvts[2];
> +
> +  struct cmsghdr *cmsg, *last = NULL;
> +  int type = 0;
> +
> +  for (cmsg = CMSG_FIRSTHDR (msg);
> +       cmsg != NULL;
> +       cmsg = CMSG_NXTHDR (msg, cmsg))
> +    {
> +      if (cmsg->cmsg_level != SOL_SOCKET)
> +	continue;
> +
> +      switch (cmsg->cmsg_type)
> +	{
> +	case COMPAT_SO_TIMESTAMP_OLD:
> +	  if (type != 0)
> +	    break;
> +	  type = COMPAT_SO_TIMESTAMP_NEW;
> +	  goto common;
> +
> +	case COMPAT_SO_TIMESTAMPNS_OLD:
> +	  type = COMPAT_SO_TIMESTAMPNS_NEW;
> +
> +	/* fallthrough  */
> +	common:
> +	  memcpy (tvts, CMSG_DATA (cmsg), sizeof (tvts));
> +	  break;
> +	}
> +
> +      last = cmsg;
> +    }
> +
> +  if (last == NULL || type == 0)
> +    return;
> +
> +  if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen)
> +    {
> +      msg->msg_flags |= MSG_CTRUNC;
> +      return;
> +    }
> +
> +  msg->msg_controllen += CMSG_SPACE (sizeof tvts);
> +  cmsg = CMSG_NXTHDR(msg, last);
> +  cmsg->cmsg_level = SOL_SOCKET;
> +  cmsg->cmsg_type = type;
> +  cmsg->cmsg_len = CMSG_LEN (sizeof tvts);
> +  memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts);
> +}
> +libc_hidden_def (__convert_scm_timestamps)
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/getsockopt.c b/sysdeps/unix/sysv/linux/getsockopt.c
> index c8e502d976..14b782d0da 100644
> --- a/sysdeps/unix/sysv/linux/getsockopt.c
> +++ b/sysdeps/unix/sysv/linux/getsockopt.c
> @@ -67,6 +67,18 @@ getsockopt32 (int fd, int level, int optname, void *optval,
>  	*tv64 = valid_timeval32_to_timeval64 (tv32);
>  	*len = sizeof (*tv64);
>        }
> +      break;
> +
> +    case COMPAT_SO_TIMESTAMP_NEW:
> +    case COMPAT_SO_TIMESTAMPNS_NEW:
> +      {
> +	if (optname == COMPAT_SO_TIMESTAMP_NEW)
> +	  optname = COMPAT_SO_TIMESTAMP_OLD;
> +	if (optname == COMPAT_SO_TIMESTAMPNS_NEW)
> +	  optname = COMPAT_SO_TIMESTAMPNS_OLD;
> +	r = getsockopt_syscall (fd, level, optname, optval, len);
> +      }
> +      break;
>      }
>  
>    return r;
> diff --git a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> index ea721e0fc2..f3b98012d5 100644
> --- a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> +++ b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
> @@ -27,4 +27,9 @@
>  #define COMPAT_SO_RCVTIMEO_NEW 16448
>  #define COMPAT_SO_SNDTIMEO_NEW 16449
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 0x4012
> +#define COMPAT_SO_TIMESTAMPNS_OLD 0x4013
> +#define COMPAT_SO_TIMESTAMP_NEW 0x4038
> +#define COMPAT_SO_TIMESTAMPNS_NEW 0x4039
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> index ab8bd62853..31fa69fa9f 100644
> --- a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> +++ b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
> @@ -27,4 +27,9 @@
>  #define COMPAT_SO_RCVTIMEO_NEW 66
>  #define COMPAT_SO_SNDTIMEO_NEW 67
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 29
> +#define COMPAT_SO_TIMESTAMPNS_OLD 35
> +#define COMPAT_SO_TIMESTAMP_NEW 63
> +#define COMPAT_SO_TIMESTAMPNS_NEW 64
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> index 1e48dcca8d..889251895b 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> +++ b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
> @@ -27,4 +27,9 @@
>  #define COMPAT_SO_RCVTIMEO_NEW 66
>  #define COMPAT_SO_SNDTIMEO_NEW 67
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 29
> +#define COMPAT_SO_TIMESTAMPNS_OLD 35
> +#define COMPAT_SO_TIMESTAMP_NEW 63
> +#define COMPAT_SO_TIMESTAMPNS_NEW 64
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/recvmsg.c b/sysdeps/unix/sysv/linux/recvmsg.c
> index b209b4ad99..a2a600228b 100644
> --- a/sysdeps/unix/sysv/linux/recvmsg.c
> +++ b/sysdeps/unix/sysv/linux/recvmsg.c
> @@ -19,16 +19,27 @@
>  #include <sys/socket.h>
>  #include <sysdep-cancel.h>
>  #include <socketcall.h>
> -#include <shlib-compat.h>
>  
>  ssize_t
>  __libc_recvmsg (int fd, struct msghdr *msg, int flags)
>  {
> -# ifdef __ASSUME_RECVMSG_SYSCALL
> -  return SYSCALL_CANCEL (recvmsg, fd, msg, flags);
> -# else
> -  return SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
> -# endif
> +  ssize_t r;
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +  socklen_t orig_controllen = msg->msg_controllen;
> +#endif
> +
> +#ifdef __ASSUME_RECVMSG_SYSCALL
> +  r = SYSCALL_CANCEL (recvmsg, fd, msg, flags);
> +#else
> +  r = SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
> +#endif
> +
> +#ifndef __ASSUME_TIME64_SYSCALLS
> +  if (r >= 0)
> +    __convert_scm_timestamps (msg, orig_controllen);
> +#endif
> +
> +  return r;
>  }
>  weak_alias (__libc_recvmsg, recvmsg)
>  weak_alias (__libc_recvmsg, __recvmsg)
> diff --git a/sysdeps/unix/sysv/linux/setsockopt.c b/sysdeps/unix/sysv/linux/setsockopt.c
> index 6505202265..a4780a9d33 100644
> --- a/sysdeps/unix/sysv/linux/setsockopt.c
> +++ b/sysdeps/unix/sysv/linux/setsockopt.c
> @@ -69,6 +69,18 @@ setsockopt32 (int fd, int level, int optname, const void *optval,
>  
>  	r = setsockopt_syscall (fd, level, optname, &tv32, sizeof (tv32));
>        }
> +      break;
> +
> +    case COMPAT_SO_TIMESTAMP_NEW:
> +    case COMPAT_SO_TIMESTAMPNS_NEW:
> +      {
> +	if (optname == COMPAT_SO_TIMESTAMP_NEW)
> +	  optname = COMPAT_SO_TIMESTAMP_OLD;
> +	if (optname == COMPAT_SO_TIMESTAMPNS_NEW)
> +	  optname = COMPAT_SO_TIMESTAMPNS_OLD;
> +	r = setsockopt_syscall (fd, level, optname, NULL, 0);
> +      }
> +      break;
>      }
>  
>    return r;
> diff --git a/sysdeps/unix/sysv/linux/socket-constants-time64.h b/sysdeps/unix/sysv/linux/socket-constants-time64.h
> index e5a3777f28..7f7ca05504 100644
> --- a/sysdeps/unix/sysv/linux/socket-constants-time64.h
> +++ b/sysdeps/unix/sysv/linux/socket-constants-time64.h
> @@ -27,4 +27,9 @@
>  #define COMPAT_SO_RCVTIMEO_NEW 66
>  #define COMPAT_SO_SNDTIMEO_NEW 67
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 29
> +#define COMPAT_SO_TIMESTAMPNS_OLD 35
> +#define COMPAT_SO_TIMESTAMP_NEW 63
> +#define COMPAT_SO_TIMESTAMPNS_NEW 64
> +
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> index b137abdeea..56358923e1 100644
> --- a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> +++ b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
> @@ -27,4 +27,9 @@
>  #define COMPAT_SO_RCVTIMEO_NEW 68
>  #define COMPAT_SO_SNDTIMEO_NEW 69
>  
> +#define COMPAT_SO_TIMESTAMP_OLD 0x001d
> +#define COMPAT_SO_TIMESTAMPNS_OLD 0x0021
> +#define COMPAT_SO_TIMESTAMP_NEW 0x0046
> +#define COMPAT_SO_TIMESTAMPNS_NEW 0x0042
> +
>  #endif
>
diff mbox series

Patch

diff --git a/include/sys/socket.h b/include/sys/socket.h
index 0e39dd2a3a..15d4a62b26 100644
--- a/include/sys/socket.h
+++ b/include/sys/socket.h
@@ -164,5 +164,10 @@  libc_hidden_proto (__libc_sa_len)
 
 libc_hidden_proto (__cmsg_nxthdr)
 
+#ifndef __ASSUME_TIME64_SYSCALLS
+extern void __convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize) ;
+libc_hidden_proto (__convert_scm_timestamps)
+#endif
+
 #endif
 #endif
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index fb155cf856..e28f6470e3 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -64,7 +64,7 @@  sysdep_routines += adjtimex clone umount umount2 readahead sysctl \
 		   time64-support pselect32 \
 		   xstat fxstat lxstat xstat64 fxstat64 lxstat64 \
 		   fxstatat fxstatat64 \
-		   xmknod xmknodat
+		   xmknod xmknodat convert_scm_timestamps
 
 CFLAGS-gethostid.c = -fexceptions
 CFLAGS-tee.c = -fexceptions -fasynchronous-unwind-tables
diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions
index 220bb2dffe..148f04c50a 100644
--- a/sysdeps/unix/sysv/linux/Versions
+++ b/sysdeps/unix/sysv/linux/Versions
@@ -177,6 +177,7 @@  libc {
     __pread64_nocancel;
     __close_nocancel;
     __sigtimedwait;
+    __convert_scm_timestamps;
     # functions used by nscd
     __netlink_assert_response;
   }
diff --git a/sysdeps/unix/sysv/linux/convert_scm_timestamps.c b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c
new file mode 100644
index 0000000000..3c123c28ce
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/convert_scm_timestamps.c
@@ -0,0 +1,96 @@ 
+/* Socket timestamp conversion routines.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <kernel-features.h>
+
+#ifndef __ASSUME_TIME64_SYSCALLS
+# include <stdint.h>
+# include <string.h>
+# include <sys/socket.h>
+# include <socket-constants-time64.h>
+
+/* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and
+   appends it to the control buffer.  The 32-bit time field is kept as-is.
+
+   Calls with __TIMESIZE=32 will see the converted 64-bit time control
+   messages as spurious control message of unknown type.
+
+   Calls with __TIMESIZE=64 running on pre-time64 kernels will see the
+   original message as a spurious control ones of unknown typ while running
+   on kernel with native 64-bit time support will only see the time64 version
+   of the control message.  */
+void
+__convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize)
+{
+  if (msg->msg_control == NULL || msg->msg_controllen == 0)
+    return;
+
+  /* The returned control message format for SO_TIMESTAMP_NEW is a
+     'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a
+     'struct __kernel_timespec'.  In either case it is two uint64_t
+     members.  */
+  uint64_t tvts[2];
+
+  struct cmsghdr *cmsg, *last = NULL;
+  int type = 0;
+
+  for (cmsg = CMSG_FIRSTHDR (msg);
+       cmsg != NULL;
+       cmsg = CMSG_NXTHDR (msg, cmsg))
+    {
+      if (cmsg->cmsg_level != SOL_SOCKET)
+	continue;
+
+      switch (cmsg->cmsg_type)
+	{
+	case COMPAT_SO_TIMESTAMP_OLD:
+	  if (type != 0)
+	    break;
+	  type = COMPAT_SO_TIMESTAMP_NEW;
+	  goto common;
+
+	case COMPAT_SO_TIMESTAMPNS_OLD:
+	  type = COMPAT_SO_TIMESTAMPNS_NEW;
+
+	/* fallthrough  */
+	common:
+	  memcpy (tvts, CMSG_DATA (cmsg), sizeof (tvts));
+	  break;
+	}
+
+      last = cmsg;
+    }
+
+  if (last == NULL || type == 0)
+    return;
+
+  if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen)
+    {
+      msg->msg_flags |= MSG_CTRUNC;
+      return;
+    }
+
+  msg->msg_controllen += CMSG_SPACE (sizeof tvts);
+  cmsg = CMSG_NXTHDR(msg, last);
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = type;
+  cmsg->cmsg_len = CMSG_LEN (sizeof tvts);
+  memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts);
+}
+libc_hidden_def (__convert_scm_timestamps)
+#endif
diff --git a/sysdeps/unix/sysv/linux/getsockopt.c b/sysdeps/unix/sysv/linux/getsockopt.c
index c8e502d976..14b782d0da 100644
--- a/sysdeps/unix/sysv/linux/getsockopt.c
+++ b/sysdeps/unix/sysv/linux/getsockopt.c
@@ -67,6 +67,18 @@  getsockopt32 (int fd, int level, int optname, void *optval,
 	*tv64 = valid_timeval32_to_timeval64 (tv32);
 	*len = sizeof (*tv64);
       }
+      break;
+
+    case COMPAT_SO_TIMESTAMP_NEW:
+    case COMPAT_SO_TIMESTAMPNS_NEW:
+      {
+	if (optname == COMPAT_SO_TIMESTAMP_NEW)
+	  optname = COMPAT_SO_TIMESTAMP_OLD;
+	if (optname == COMPAT_SO_TIMESTAMPNS_NEW)
+	  optname = COMPAT_SO_TIMESTAMPNS_OLD;
+	r = getsockopt_syscall (fd, level, optname, optval, len);
+      }
+      break;
     }
 
   return r;
diff --git a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
index ea721e0fc2..f3b98012d5 100644
--- a/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
+++ b/sysdeps/unix/sysv/linux/hppa/socket-constants-time64.h
@@ -27,4 +27,9 @@ 
 #define COMPAT_SO_RCVTIMEO_NEW 16448
 #define COMPAT_SO_SNDTIMEO_NEW 16449
 
+#define COMPAT_SO_TIMESTAMP_OLD 0x4012
+#define COMPAT_SO_TIMESTAMPNS_OLD 0x4013
+#define COMPAT_SO_TIMESTAMP_NEW 0x4038
+#define COMPAT_SO_TIMESTAMPNS_NEW 0x4039
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
index ab8bd62853..31fa69fa9f 100644
--- a/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
+++ b/sysdeps/unix/sysv/linux/mips/socket-constants-time64.h
@@ -27,4 +27,9 @@ 
 #define COMPAT_SO_RCVTIMEO_NEW 66
 #define COMPAT_SO_SNDTIMEO_NEW 67
 
+#define COMPAT_SO_TIMESTAMP_OLD 29
+#define COMPAT_SO_TIMESTAMPNS_OLD 35
+#define COMPAT_SO_TIMESTAMP_NEW 63
+#define COMPAT_SO_TIMESTAMPNS_NEW 64
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
index 1e48dcca8d..889251895b 100644
--- a/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
+++ b/sysdeps/unix/sysv/linux/powerpc/socket-constants-time64.h
@@ -27,4 +27,9 @@ 
 #define COMPAT_SO_RCVTIMEO_NEW 66
 #define COMPAT_SO_SNDTIMEO_NEW 67
 
+#define COMPAT_SO_TIMESTAMP_OLD 29
+#define COMPAT_SO_TIMESTAMPNS_OLD 35
+#define COMPAT_SO_TIMESTAMP_NEW 63
+#define COMPAT_SO_TIMESTAMPNS_NEW 64
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/recvmsg.c b/sysdeps/unix/sysv/linux/recvmsg.c
index b209b4ad99..a2a600228b 100644
--- a/sysdeps/unix/sysv/linux/recvmsg.c
+++ b/sysdeps/unix/sysv/linux/recvmsg.c
@@ -19,16 +19,27 @@ 
 #include <sys/socket.h>
 #include <sysdep-cancel.h>
 #include <socketcall.h>
-#include <shlib-compat.h>
 
 ssize_t
 __libc_recvmsg (int fd, struct msghdr *msg, int flags)
 {
-# ifdef __ASSUME_RECVMSG_SYSCALL
-  return SYSCALL_CANCEL (recvmsg, fd, msg, flags);
-# else
-  return SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
-# endif
+  ssize_t r;
+#ifndef __ASSUME_TIME64_SYSCALLS
+  socklen_t orig_controllen = msg->msg_controllen;
+#endif
+
+#ifdef __ASSUME_RECVMSG_SYSCALL
+  r = SYSCALL_CANCEL (recvmsg, fd, msg, flags);
+#else
+  r = SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
+#endif
+
+#ifndef __ASSUME_TIME64_SYSCALLS
+  if (r >= 0)
+    __convert_scm_timestamps (msg, orig_controllen);
+#endif
+
+  return r;
 }
 weak_alias (__libc_recvmsg, recvmsg)
 weak_alias (__libc_recvmsg, __recvmsg)
diff --git a/sysdeps/unix/sysv/linux/setsockopt.c b/sysdeps/unix/sysv/linux/setsockopt.c
index 6505202265..a4780a9d33 100644
--- a/sysdeps/unix/sysv/linux/setsockopt.c
+++ b/sysdeps/unix/sysv/linux/setsockopt.c
@@ -69,6 +69,18 @@  setsockopt32 (int fd, int level, int optname, const void *optval,
 
 	r = setsockopt_syscall (fd, level, optname, &tv32, sizeof (tv32));
       }
+      break;
+
+    case COMPAT_SO_TIMESTAMP_NEW:
+    case COMPAT_SO_TIMESTAMPNS_NEW:
+      {
+	if (optname == COMPAT_SO_TIMESTAMP_NEW)
+	  optname = COMPAT_SO_TIMESTAMP_OLD;
+	if (optname == COMPAT_SO_TIMESTAMPNS_NEW)
+	  optname = COMPAT_SO_TIMESTAMPNS_OLD;
+	r = setsockopt_syscall (fd, level, optname, NULL, 0);
+      }
+      break;
     }
 
   return r;
diff --git a/sysdeps/unix/sysv/linux/socket-constants-time64.h b/sysdeps/unix/sysv/linux/socket-constants-time64.h
index e5a3777f28..7f7ca05504 100644
--- a/sysdeps/unix/sysv/linux/socket-constants-time64.h
+++ b/sysdeps/unix/sysv/linux/socket-constants-time64.h
@@ -27,4 +27,9 @@ 
 #define COMPAT_SO_RCVTIMEO_NEW 66
 #define COMPAT_SO_SNDTIMEO_NEW 67
 
+#define COMPAT_SO_TIMESTAMP_OLD 29
+#define COMPAT_SO_TIMESTAMPNS_OLD 35
+#define COMPAT_SO_TIMESTAMP_NEW 63
+#define COMPAT_SO_TIMESTAMPNS_NEW 64
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
index b137abdeea..56358923e1 100644
--- a/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
+++ b/sysdeps/unix/sysv/linux/sparc/socket-constants-time64.h
@@ -27,4 +27,9 @@ 
 #define COMPAT_SO_RCVTIMEO_NEW 68
 #define COMPAT_SO_SNDTIMEO_NEW 69
 
+#define COMPAT_SO_TIMESTAMP_OLD 0x001d
+#define COMPAT_SO_TIMESTAMPNS_OLD 0x0021
+#define COMPAT_SO_TIMESTAMP_NEW 0x0046
+#define COMPAT_SO_TIMESTAMPNS_NEW 0x0042
+
 #endif