1 | /* Socket timestamp conversion routines. |
2 | Copyright (C) 2021-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 <bits/timesize.h> |
20 | |
21 | #if __TIMESIZE != 64 |
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 | |