| 1 | /* Socket timestamp conversion routines. |
| 2 | Copyright (C) 2021-2022 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 <kernel-features.h> |
| 20 | |
| 21 | #ifndef __ASSUME_TIME64_SYSCALLS |
| 22 | # include <stdint.h> |
| 23 | # include <string.h> |
| 24 | # include <sys/socket.h> |
| 25 | # include <socket-constants-time64.h> |
| 26 | |
| 27 | /* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and |
| 28 | appends it to the control buffer. The 32-bit time field is kept as-is. |
| 29 | |
| 30 | Calls with __TIMESIZE=32 will see the converted 64-bit time control |
| 31 | messages as spurious control message of unknown type. |
| 32 | |
| 33 | Calls with __TIMESIZE=64 running on pre-time64 kernels will see the |
| 34 | original message as a spurious control ones of unknown typ while running |
| 35 | on kernel with native 64-bit time support will only see the time64 version |
| 36 | of the control message. */ |
| 37 | void |
| 38 | __convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize) |
| 39 | { |
| 40 | if (msg->msg_control == NULL || msg->msg_controllen == 0) |
| 41 | return; |
| 42 | |
| 43 | /* The returned control message format for SO_TIMESTAMP_NEW is a |
| 44 | 'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a |
| 45 | 'struct __kernel_timespec'. In either case it is two uint64_t |
| 46 | members. */ |
| 47 | int64_t tvts[2]; |
| 48 | int32_t tmp[2]; |
| 49 | |
| 50 | struct cmsghdr *cmsg, *last = NULL; |
| 51 | int type = 0; |
| 52 | |
| 53 | for (cmsg = CMSG_FIRSTHDR (msg); |
| 54 | cmsg != NULL; |
| 55 | cmsg = CMSG_NXTHDR (msg, cmsg)) |
| 56 | { |
| 57 | last = cmsg; |
| 58 | |
| 59 | if (cmsg->cmsg_level != SOL_SOCKET) |
| 60 | continue; |
| 61 | |
| 62 | switch (cmsg->cmsg_type) |
| 63 | { |
| 64 | case COMPAT_SO_TIMESTAMP_OLD: |
| 65 | if (type != 0) |
| 66 | break; |
| 67 | type = COMPAT_SO_TIMESTAMP_NEW; |
| 68 | goto common; |
| 69 | |
| 70 | case COMPAT_SO_TIMESTAMPNS_OLD: |
| 71 | type = COMPAT_SO_TIMESTAMPNS_NEW; |
| 72 | |
| 73 | /* fallthrough */ |
| 74 | common: |
| 75 | memcpy (tmp, CMSG_DATA (cmsg), sizeof (tmp)); |
| 76 | tvts[0] = tmp[0]; |
| 77 | tvts[1] = tmp[1]; |
| 78 | break; |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | if (type == 0) |
| 83 | return; |
| 84 | |
| 85 | if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen) |
| 86 | { |
| 87 | msg->msg_flags |= MSG_CTRUNC; |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | /* Zero memory for the new cmsghdr, so reading cmsg_len field |
| 92 | by CMSG_NXTHDR does not trigger UB. */ |
| 93 | memset (msg->msg_control + msg->msg_controllen, 0, |
| 94 | CMSG_SPACE (sizeof tvts)); |
| 95 | msg->msg_controllen += CMSG_SPACE (sizeof tvts); |
| 96 | cmsg = CMSG_NXTHDR (msg, last); |
| 97 | cmsg->cmsg_level = SOL_SOCKET; |
| 98 | cmsg->cmsg_type = type; |
| 99 | cmsg->cmsg_len = CMSG_LEN (sizeof tvts); |
| 100 | memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts); |
| 101 | } |
| 102 | #endif |
| 103 | |