1 /* Check recvmsg/recvmmsg 64-bit timestamp support.
2 Copyright (C) 2022-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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/>. */
19 #include <array_length.h>
20 #include <arpa/inet.h>
24 #include <support/check.h>
25 #include <support/next_to_fault.h>
26 #include <support/support.h>
27 #include <support/test-driver.h>
28 #include <support/xunistd.h>
29 #include <support/xsocket.h>
32 /* Some extra space added for ancillary data, it might be used to convert
33 32-bit timestamp to 64-bit for _TIME_BITS=64. */
34 enum { slack_max_size
= 64 };
35 static const int slack
[] = { 0, 4, 8, 16, 32, slack_max_size
};
37 static bool support_64_timestamp
;
38 /* AF_INET socket and address used to receive data. */
40 static struct sockaddr_in srv_addr
;
43 do_sendto (const struct sockaddr_in
*addr
, int nmsgs
)
45 int s
= xsocket (AF_INET
, SOCK_DGRAM
| SOCK_CLOEXEC
, 0);
46 xconnect (s
, (const struct sockaddr
*) addr
, sizeof (*addr
));
48 for (int i
= 0; i
< nmsgs
; i
++)
49 xsendto (s
, &i
, sizeof (i
), 0, (const struct sockaddr
*) addr
,
58 do_recvmsg_slack_ancillary (bool use_multi_call
, int s
, void *cmsg
,
59 size_t slack
, size_t tsize
, int exp_payload
)
65 .iov_len
= sizeof (payload
)
67 size_t msg_controllen
= CMSG_SPACE (tsize
) + slack
;
68 char *msg_control
= cmsg
- msg_controllen
;
69 memset (msg_control
, 0x55, msg_controllen
);
70 struct mmsghdr mmhdr
=
78 .msg_control
= msg_control
,
79 .msg_controllen
= msg_controllen
86 r
= recvmmsg (s
, &mmhdr
, 1, 0, NULL
);
91 r
= recvmsg (s
, &mmhdr
.msg_hdr
, 0);
92 TEST_COMPARE (r
, sizeof (int));
93 TEST_COMPARE (payload
, exp_payload
);
98 /* A timestamp is expected if 32-bit timestamp are used (support in every
99 configuration) or if underlying kernel support 64-bit timestamps.
100 Otherwise recvmsg will need extra space do add the 64-bit timestamp. */
102 if (sizeof (time_t) == 4 || support_64_timestamp
)
103 exp_timestamp
= true;
105 exp_timestamp
= slack
>= CMSG_SPACE (tsize
);
107 bool timestamp
= false;
108 for (struct cmsghdr
*cmsg
= CMSG_FIRSTHDR (&mmhdr
.msg_hdr
);
110 cmsg
= CMSG_NXTHDR (&mmhdr
.msg_hdr
, cmsg
))
112 if (cmsg
->cmsg_level
!= SOL_SOCKET
)
114 if (cmsg
->cmsg_type
== SCM_TIMESTAMP
115 && cmsg
->cmsg_len
== CMSG_LEN (sizeof (struct timeval
)))
118 memcpy (&tv
, CMSG_DATA (cmsg
), sizeof (tv
));
120 printf ("SCM_TIMESTAMP: {%jd, %jd}\n", (intmax_t)tv
.tv_sec
,
121 (intmax_t)tv
.tv_usec
);
124 else if (cmsg
->cmsg_type
== SCM_TIMESTAMPNS
125 && cmsg
->cmsg_len
== CMSG_LEN (sizeof (struct timespec
)))
128 memcpy (&ts
, CMSG_DATA (cmsg
), sizeof (ts
));
130 printf ("SCM_TIMESTAMPNS: {%jd, %jd}\n", (intmax_t)ts
.tv_sec
,
131 (intmax_t)ts
.tv_nsec
);
136 TEST_COMPARE (timestamp
, exp_timestamp
);
139 /* Check if the extra ancillary space is correctly handled by recvmsg and
140 recvmmsg with different extra space for the ancillaty buffer. */
142 do_test_slack_space (void)
144 /* Setup the ancillary data buffer with an extra page with PROT_NONE to
145 check the possible timestamp conversion on some systems. */
146 struct support_next_to_fault nf
=
147 support_next_to_fault_allocate (slack_max_size
);
148 void *msgbuf
= nf
.buffer
+ slack_max_size
;
150 /* Enable the timestamp using struct timeval precision. */
152 int r
= setsockopt (srv
, SOL_SOCKET
, SO_TIMESTAMP
, &(int){1},
154 TEST_VERIFY_EXIT (r
!= -1);
157 do_sendto (&srv_addr
, array_length (slack
));
158 for (int s
= 0; s
< array_length (slack
); s
++)
160 memset (nf
.buffer
, 0x55, nf
.length
);
161 do_recvmsg_slack_ancillary (false, srv
, msgbuf
, slack
[s
],
162 sizeof (struct timeval
), s
);
164 /* Check recvmmsg. */
165 do_sendto (&srv_addr
, array_length (slack
));
166 for (int s
= 0; s
< array_length (slack
); s
++)
168 memset (nf
.buffer
, 0x55, nf
.length
);
169 do_recvmsg_slack_ancillary (true, srv
, msgbuf
, slack
[s
],
170 sizeof (struct timeval
), s
);
173 /* Now enable timestamp using a higher precision, it overwrites the previous
176 int r
= setsockopt (srv
, SOL_SOCKET
, SO_TIMESTAMPNS
, &(int){1},
178 TEST_VERIFY_EXIT (r
!= -1);
181 do_sendto (&srv_addr
, array_length (slack
));
182 for (int s
= 0; s
< array_length (slack
); s
++)
183 do_recvmsg_slack_ancillary (false, srv
, msgbuf
, slack
[s
],
184 sizeof (struct timespec
), s
);
185 /* Check recvmmsg. */
186 do_sendto (&srv_addr
, array_length (slack
));
187 for (int s
= 0; s
< array_length (slack
); s
++)
188 do_recvmsg_slack_ancillary (true, srv
, msgbuf
, slack
[s
],
189 sizeof (struct timespec
), s
);
191 support_next_to_fault_free (&nf
);
194 /* Check if the converted 64-bit timestamp is correctly appended when there
195 are multiple ancillary messages. */
197 do_recvmsg_multiple_ancillary (bool use_multi_call
, int s
, void *cmsg
,
198 size_t cmsgsize
, int exp_msg
)
204 .iov_len
= sizeof (msg
)
206 size_t msgs
= cmsgsize
;
207 struct mmsghdr mmhdr
=
215 .msg_controllen
= msgs
,
223 r
= recvmmsg (s
, &mmhdr
, 1, 0, NULL
);
228 r
= recvmsg (s
, &mmhdr
.msg_hdr
, 0);
229 TEST_COMPARE (r
, sizeof (int));
230 TEST_COMPARE (msg
, exp_msg
);
235 bool timestamp
= false;
236 bool origdstaddr
= false;
237 for (struct cmsghdr
*cmsg
= CMSG_FIRSTHDR (&mmhdr
.msg_hdr
);
239 cmsg
= CMSG_NXTHDR (&mmhdr
.msg_hdr
, cmsg
))
241 if (cmsg
->cmsg_level
== SOL_IP
242 && cmsg
->cmsg_type
== IP_ORIGDSTADDR
243 && cmsg
->cmsg_len
>= CMSG_LEN (sizeof (struct sockaddr_in
)))
245 struct sockaddr_in sa
;
246 memcpy (&sa
, CMSG_DATA (cmsg
), sizeof (sa
));
249 char str
[INET_ADDRSTRLEN
];
250 inet_ntop (AF_INET
, &sa
.sin_addr
, str
, INET_ADDRSTRLEN
);
251 printf ("IP_ORIGDSTADDR: %s:%d\n", str
, ntohs (sa
.sin_port
));
253 origdstaddr
= sa
.sin_addr
.s_addr
== srv_addr
.sin_addr
.s_addr
254 && sa
.sin_port
== srv_addr
.sin_port
;
256 if (cmsg
->cmsg_level
== SOL_SOCKET
257 && cmsg
->cmsg_type
== SCM_TIMESTAMP
258 && cmsg
->cmsg_len
>= CMSG_LEN (sizeof (struct timeval
)))
261 memcpy (&tv
, CMSG_DATA (cmsg
), sizeof (tv
));
263 printf ("SCM_TIMESTAMP: {%jd, %jd}\n", (intmax_t)tv
.tv_sec
,
264 (intmax_t)tv
.tv_usec
);
269 TEST_COMPARE (timestamp
, true);
270 TEST_COMPARE (origdstaddr
, true);
274 do_test_multiple_ancillary (void)
277 int r
= setsockopt (srv
, SOL_SOCKET
, SO_TIMESTAMP
, &(int){1},
279 TEST_VERIFY_EXIT (r
!= -1);
282 int r
= setsockopt (srv
, IPPROTO_IP
, IP_RECVORIGDSTADDR
, &(int){1},
284 TEST_VERIFY_EXIT (r
!= -1);
287 /* Enougth data for default SO_TIMESTAMP, the IP_RECVORIGDSTADDR, and the
288 extra 64-bit SO_TIMESTAMP. */
289 enum { msgbuflen
= CMSG_SPACE (2 * sizeof (uint64_t))
290 + CMSG_SPACE (sizeof (struct sockaddr_in
))
291 + CMSG_SPACE (2 * sizeof (uint64_t)) };
292 char msgbuf
[msgbuflen
];
296 do_sendto (&srv_addr
, nmsgs
);
297 for (int s
= 0; s
< nmsgs
; s
++)
298 do_recvmsg_multiple_ancillary (false, srv
, msgbuf
, msgbuflen
, s
);
299 /* Check recvmmsg. */
300 do_sendto (&srv_addr
, nmsgs
);
301 for (int s
= 0; s
< nmsgs
; s
++)
302 do_recvmsg_multiple_ancillary (true, srv
, msgbuf
, msgbuflen
, s
);
308 srv
= xsocket (AF_INET
, SOCK_DGRAM
, 0);
309 srv_addr
= (struct sockaddr_in
) {
310 .sin_family
= AF_INET
,
311 .sin_addr
= {.s_addr
= htonl (INADDR_LOOPBACK
) },
313 xbind (srv
, (struct sockaddr
*) &srv_addr
, sizeof (srv_addr
));
315 socklen_t sa_len
= sizeof (srv_addr
);
316 xgetsockname (srv
, (struct sockaddr
*) &srv_addr
, &sa_len
);
317 TEST_VERIFY (sa_len
== sizeof (srv_addr
));
320 TEST_COMPARE (recvmsg (-1, NULL
, 0), -1);
321 TEST_COMPARE (errno
, EBADF
);
322 TEST_COMPARE (recvmmsg (-1, NULL
, 0, 0, NULL
), -1);
323 TEST_COMPARE (errno
, EBADF
);
325 /* If underlying kernel does not support */
326 support_64_timestamp
= support_socket_so_timestamp_time64 (srv
);
328 do_test_slack_space ();
329 do_test_multiple_ancillary ();
336 #include <support/test-driver.c>