1/* Check recvmsg/recvmmsg 64-bit timestamp support.
2 Copyright (C) 2022-2024 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. */
28static int srv;
29static struct sockaddr_in srv_addr;
30
31static int
32do_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
45static void
46do_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 (fd: srv, vmessages: mmhdr, vlen: 1, flags: 0, NULL);
65 if (r >= 0)
66 r = mmhdr->msg_len;
67 }
68 else
69 r = recvmsg (fd: srv, message: &mmhdr->msg_hdr, flags: 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. */
76static void
77do_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 (fd: srv, SOL_SOCKET, COMPAT_SO_TIMESTAMP_OLD, optval: &(int){1},
93 optlen: sizeof (int));
94 TEST_VERIFY_EXIT (r != -1);
95
96 do_sendto (addr: &srv_addr, payload: 42);
97 do_recvmsg_ancillary (use_multi_call: mc, mmhdr: &mmhdr, msgbuf: &control, msgbuflen: sizeof control, exp_payload: 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 (fd: srv, SOL_SOCKET, COMPAT_SO_TIMESTAMPNS_OLD, optval: &(int){1},
119 optlen: sizeof (int));
120 TEST_VERIFY_EXIT (r != -1);
121
122 do_sendto (addr: &srv_addr, payload: 42);
123 do_recvmsg_ancillary (use_multi_call: mc, mmhdr: &mmhdr, msgbuf: &control, msgbuflen: sizeof control, exp_payload: 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. */
147static void
148do_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 (fd: srv, SOL_SOCKET, COMPAT_SO_TIMESTAMP_OLD, optval: &(int){1},
156 optlen: 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 (addr: &srv_addr, payload: 42);
166 do_recvmsg_ancillary (use_multi_call: mc, mmhdr: &mmhdr, msgbuf: &control, msgbuflen: sizeof control, exp_payload: 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 (fd: srv, SOL_SOCKET, COMPAT_SO_TIMESTAMPNS_OLD, optval: &(int){1},
197 optlen: 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 (addr: &srv_addr, payload: 42);
207 do_recvmsg_ancillary (use_multi_call: mc, mmhdr: &mmhdr, msgbuf: &control, msgbuflen: sizeof control, exp_payload: 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
236static int
237do_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>
267

source code of glibc/sysdeps/unix/sysv/linux/tst-socket-timestamp-compat.c