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 | # include <libc-diag.h> |
27 | |
28 | /* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and |
29 | appends it to the control buffer. The 32-bit time field is kept as-is. |
30 | |
31 | Calls with __TIMESIZE=32 will see the converted 64-bit time control |
32 | messages as spurious control message of unknown type. |
33 | |
34 | Calls with __TIMESIZE=64 running on pre-time64 kernels will see the |
35 | original message as a spurious control ones of unknown typ while running |
36 | on kernel with native 64-bit time support will only see the time64 version |
37 | of the control message. */ |
38 | void |
39 | __convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize) |
40 | { |
41 | if (msg->msg_control == NULL || msg->msg_controllen == 0) |
42 | return; |
43 | |
44 | /* The returned control message format for SO_TIMESTAMP_NEW is a |
45 | 'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a |
46 | 'struct __kernel_timespec'. In either case it is two uint64_t |
47 | members. */ |
48 | |
49 | /* GCC 6 issues an warning that tvts[0]/tvts[1] maybe be used uninitialized, |
50 | however it would be used if type is set to a value different than 0 |
51 | (done by either COMPAT_SO_TIMESTAMP_OLD or COMPAT_SO_TIMESTAMPNS_OLD) |
52 | which will fallthrough to 'common' label. */ |
53 | DIAG_PUSH_NEEDS_COMMENT; |
54 | DIAG_IGNORE_NEEDS_COMMENT (6, "-Wmaybe-uninitialized" ); |
55 | int64_t tvts[2]; |
56 | DIAG_POP_NEEDS_COMMENT; |
57 | int32_t tmp[2]; |
58 | |
59 | struct cmsghdr *cmsg, *last = NULL; |
60 | int type = 0; |
61 | |
62 | for (cmsg = CMSG_FIRSTHDR (msg); |
63 | cmsg != NULL; |
64 | cmsg = CMSG_NXTHDR (msg, cmsg)) |
65 | { |
66 | last = cmsg; |
67 | |
68 | if (cmsg->cmsg_level != SOL_SOCKET) |
69 | continue; |
70 | |
71 | switch (cmsg->cmsg_type) |
72 | { |
73 | case COMPAT_SO_TIMESTAMP_OLD: |
74 | if (type != 0) |
75 | break; |
76 | type = COMPAT_SO_TIMESTAMP_NEW; |
77 | goto common; |
78 | |
79 | case COMPAT_SO_TIMESTAMPNS_OLD: |
80 | type = COMPAT_SO_TIMESTAMPNS_NEW; |
81 | |
82 | /* fallthrough */ |
83 | common: |
84 | memcpy (tmp, CMSG_DATA (cmsg), sizeof (tmp)); |
85 | tvts[0] = tmp[0]; |
86 | tvts[1] = tmp[1]; |
87 | break; |
88 | } |
89 | } |
90 | |
91 | if (type == 0) |
92 | return; |
93 | |
94 | if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen) |
95 | { |
96 | msg->msg_flags |= MSG_CTRUNC; |
97 | return; |
98 | } |
99 | |
100 | /* Zero memory for the new cmsghdr, so reading cmsg_len field |
101 | by CMSG_NXTHDR does not trigger UB. */ |
102 | memset (msg->msg_control + msg->msg_controllen, 0, |
103 | CMSG_SPACE (sizeof tvts)); |
104 | msg->msg_controllen += CMSG_SPACE (sizeof tvts); |
105 | cmsg = CMSG_NXTHDR (msg, last); |
106 | cmsg->cmsg_level = SOL_SOCKET; |
107 | cmsg->cmsg_type = type; |
108 | cmsg->cmsg_len = CMSG_LEN (sizeof tvts); |
109 | memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts); |
110 | } |
111 | #endif |
112 | |