projects/linux·blob master · refs

636 lines · 13.5 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Quick test for getsockopt{_iter} tests.
4 *
5 * Each fixture targets one converted protocol and pins down the
6 * returned-length / errno semantics across buffer-size variations,
7 * an unknown optname and a bogus level.
8 *
9 * - netlink: NETLINK_PKTINFO covers the flag-style int path; the
10 * NETLINK_LIST_MEMBERSHIPS cases cover the size-discovery path
11 * that always reports the required buffer length back via optlen,
12 * even when the user buffer is too small to receive any group bits.
13 * - vsock: SO_VM_SOCKETS_BUFFER_SIZE covers the u64 path.
14 * - raw: ICMP_FILTER covers a fixed-size struct payload that clamps
15 * the length down on a short buffer instead of failing.
16 *
17 * Author: Breno Leitao <[email protected]>
18 */
19
20#include <errno.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <string.h>
24#include <unistd.h>
25#include <linux/netlink.h>
26#include <linux/rtnetlink.h>
27#include <linux/time_types.h>
28#include <linux/vm_sockets.h>
29#include <linux/icmp.h>
30#include <netinet/in.h>
31#include <netinet/tcp.h>
32#include <arpa/inet.h>
33#include <sys/socket.h>
34#include <linux/tls.h>
35#include "kselftest_harness.h"
36
37#ifndef AF_VSOCK
38#define AF_VSOCK 40
39#endif
40#ifndef SOL_RAW
41#define SOL_RAW 255
42#endif
43#ifndef ICMP_FILTER
44#define ICMP_FILTER 1
45#endif
46#ifndef IPV6_HDRINCL
47#define IPV6_HDRINCL 36
48#endif
49#ifndef IPV6_CHECKSUM
50#define IPV6_CHECKSUM 7
51#endif
52#ifndef SOL_TLS
53#define SOL_TLS 282
54#endif
55#ifndef TCP_ULP
56#define TCP_ULP 31
57#endif
58
59/* ---------- netlink ---------- */
60
61FIXTURE(netlink)
62{
63 int fd;
64};
65
66FIXTURE_SETUP(netlink)
67{
68 int group = RTNLGRP_LINK;
69
70 self->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
71 if (self->fd < 0)
72 SKIP(return, "AF_NETLINK socket: %s", strerror(errno));
73
74 /* Joining a multicast group grows nlk->ngroups so the
75 * NETLINK_LIST_MEMBERSHIPS path has a non-zero size to report.
76 */
77 if (setsockopt(self->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
78 &group, sizeof(group)) < 0)
79 SKIP(return, "NETLINK_ADD_MEMBERSHIP: %s", strerror(errno));
80}
81
82FIXTURE_TEARDOWN(netlink)
83{
84 if (self->fd >= 0)
85 close(self->fd);
86}
87
88TEST_F(netlink, pktinfo_exact)
89{
90 socklen_t optlen;
91 int val = -1;
92
93 optlen = sizeof(val);
94
95 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
96 &val, &optlen));
97 ASSERT_EQ(sizeof(int), optlen);
98 ASSERT_TRUE(val == 0 || val == 1);
99}
100
101TEST_F(netlink, pktinfo_oversize_clamped)
102{
103 char buf[16] = {};
104 socklen_t optlen;
105
106 optlen = sizeof(buf);
107
108 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
109 buf, &optlen));
110 ASSERT_EQ(sizeof(int), optlen);
111}
112
113TEST_F(netlink, pktinfo_undersize)
114{
115 char buf[2] = {};
116 socklen_t optlen;
117
118 optlen = sizeof(buf);
119
120 ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
121 buf, &optlen));
122 ASSERT_EQ(EINVAL, errno);
123 ASSERT_EQ(sizeof(buf), optlen);
124}
125
126TEST_F(netlink, list_memberships_size_discovery)
127{
128 socklen_t optlen = 0;
129 char dummy;
130
131 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK,
132 NETLINK_LIST_MEMBERSHIPS,
133 &dummy, &optlen));
134 ASSERT_GT(optlen, 0);
135 ASSERT_EQ(0, optlen % sizeof(__u32));
136}
137
138TEST_F(netlink, list_memberships_full_read)
139{
140 __u32 buf[64] = {};
141 socklen_t optlen;
142
143 optlen = sizeof(buf);
144
145 ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK,
146 NETLINK_LIST_MEMBERSHIPS,
147 buf, &optlen));
148 ASSERT_GT(optlen, 0);
149 ASSERT_LE(optlen, sizeof(buf));
150 ASSERT_EQ(0, optlen % sizeof(__u32));
151}
152
153TEST_F(netlink, bad_level)
154{
155 socklen_t optlen;
156 int val;
157
158 optlen = sizeof(val);
159
160 ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, NETLINK_PKTINFO,
161 &val, &optlen));
162 ASSERT_EQ(ENOPROTOOPT, errno);
163 ASSERT_EQ(sizeof(val), optlen);
164}
165
166TEST_F(netlink, bad_optname)
167{
168 socklen_t optlen;
169 int val;
170
171 optlen = sizeof(val);
172
173 ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, 0x7fff,
174 &val, &optlen));
175 ASSERT_EQ(ENOPROTOOPT, errno);
176 ASSERT_EQ(sizeof(val), optlen);
177}
178
179/* ---------- vsock ---------- */
180
181FIXTURE(vsock)
182{
183 int fd;
184};
185
186FIXTURE_SETUP(vsock)
187{
188 self->fd = socket(AF_VSOCK, SOCK_STREAM, 0);
189 if (self->fd < 0)
190 SKIP(return, "AF_VSOCK socket: %s", strerror(errno));
191}
192
193FIXTURE_TEARDOWN(vsock)
194{
195 if (self->fd >= 0)
196 close(self->fd);
197}
198
199TEST_F(vsock, buffer_size_exact)
200{
201 socklen_t optlen;
202 uint64_t val = 0;
203
204 optlen = sizeof(val);
205
206 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
207 SO_VM_SOCKETS_BUFFER_SIZE,
208 &val, &optlen));
209 ASSERT_EQ(sizeof(uint64_t), optlen);
210 ASSERT_GT(val, 0);
211}
212
213TEST_F(vsock, buffer_size_oversize_clamped)
214{
215 char buf[16] = {};
216 socklen_t optlen;
217
218 optlen = sizeof(buf);
219
220 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
221 SO_VM_SOCKETS_BUFFER_SIZE,
222 buf, &optlen));
223 ASSERT_EQ(sizeof(uint64_t), optlen);
224}
225
226TEST_F(vsock, buffer_size_undersize)
227{
228 char buf[4] = {};
229 socklen_t optlen;
230
231 optlen = sizeof(buf);
232
233 ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK,
234 SO_VM_SOCKETS_BUFFER_SIZE,
235 buf, &optlen));
236 ASSERT_EQ(EINVAL, errno);
237 ASSERT_EQ(sizeof(buf), optlen);
238}
239
240TEST_F(vsock, bad_level)
241{
242 socklen_t optlen;
243 uint64_t val;
244
245 optlen = sizeof(val);
246
247 ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1,
248 SO_VM_SOCKETS_BUFFER_SIZE,
249 &val, &optlen));
250 ASSERT_EQ(ENOPROTOOPT, errno);
251 ASSERT_EQ(sizeof(val), optlen);
252}
253
254TEST_F(vsock, bad_optname)
255{
256 socklen_t optlen;
257 uint64_t val;
258
259 optlen = sizeof(val);
260
261 ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 0x7fff,
262 &val, &optlen));
263 ASSERT_EQ(ENOPROTOOPT, errno);
264 ASSERT_EQ(sizeof(val), optlen);
265}
266
267/* SO_VM_SOCKETS_CONNECT_TIMEOUT_{NEW,OLD} return a sock_timeval-shaped
268 * payload, which is wider than u64 on 64-bit. They exercise the path
269 * where the protocol's reported lv (16 bytes) is larger than the
270 * common 8-byte u64 case covered above.
271 */
272TEST_F(vsock, connect_timeout_new_exact)
273{
274 struct __kernel_sock_timeval tv = {};
275 socklen_t optlen;
276
277 optlen = sizeof(tv);
278
279 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
280 SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
281 &tv, &optlen));
282 ASSERT_EQ(sizeof(tv), optlen);
283}
284
285TEST_F(vsock, connect_timeout_new_oversize_clamped)
286{
287 char buf[sizeof(struct __kernel_sock_timeval) * 2] = {};
288 socklen_t optlen;
289
290 optlen = sizeof(buf);
291
292 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
293 SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
294 buf, &optlen));
295 ASSERT_EQ(sizeof(struct __kernel_sock_timeval), optlen);
296}
297
298TEST_F(vsock, connect_timeout_new_undersize)
299{
300 socklen_t optlen;
301 uint64_t val;
302
303 optlen = sizeof(val);
304
305 ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK,
306 SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
307 &val, &optlen));
308 ASSERT_EQ(EINVAL, errno);
309 ASSERT_EQ(sizeof(val), optlen);
310}
311
312TEST_F(vsock, connect_timeout_old_exact)
313{
314 struct __kernel_old_timeval tv = {};
315 socklen_t optlen;
316
317 optlen = sizeof(tv);
318
319 ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
320 SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD,
321 &tv, &optlen));
322 ASSERT_EQ(sizeof(tv), optlen);
323}
324
325/* ---------- raw (ipv4) ---------- */
326
327FIXTURE(raw)
328{
329 int fd;
330};
331
332FIXTURE_SETUP(raw)
333{
334 struct icmp_filter filt = { .data = 0xdeadbeef };
335
336 self->fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
337 if (self->fd < 0)
338 SKIP(return, "SOCK_RAW/ICMP socket: %s", strerror(errno));
339
340 if (setsockopt(self->fd, SOL_RAW, ICMP_FILTER, &filt, sizeof(filt)) < 0)
341 SKIP(return, "set ICMP_FILTER: %s", strerror(errno));
342}
343
344FIXTURE_TEARDOWN(raw)
345{
346 if (self->fd >= 0)
347 close(self->fd);
348}
349
350TEST_F(raw, icmpfilter_exact)
351{
352 struct icmp_filter filt = {};
353 socklen_t optlen = sizeof(filt);
354
355 ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
356 &filt, &optlen));
357 ASSERT_EQ(sizeof(filt), optlen);
358 ASSERT_EQ(0xdeadbeef, filt.data);
359}
360
361TEST_F(raw, icmpfilter_oversize_clamped)
362{
363 char buf[16] = {};
364 socklen_t optlen = sizeof(buf);
365
366 ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
367 buf, &optlen));
368 ASSERT_EQ(sizeof(struct icmp_filter), optlen);
369}
370
371/* Unlike the int/u64 options above, ICMP_FILTER clamps the length down
372 * to the user buffer instead of returning EINVAL: a short buffer
373 * succeeds and reports the truncated length back via optlen.
374 */
375TEST_F(raw, icmpfilter_undersize_clamped)
376{
377 char buf[2] = {};
378 socklen_t optlen = sizeof(buf);
379
380 ASSERT_EQ(0, getsockopt(self->fd, SOL_RAW, ICMP_FILTER,
381 buf, &optlen));
382 ASSERT_EQ(sizeof(buf), optlen);
383}
384
385TEST_F(raw, icmpfilter_wrong_proto)
386{
387 struct icmp_filter filt;
388 socklen_t optlen = sizeof(filt);
389 int fd;
390
391 fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
392 if (fd < 0)
393 SKIP(return, "SOCK_RAW/UDP socket: %s", strerror(errno));
394
395 ASSERT_EQ(-1, getsockopt(fd, SOL_RAW, ICMP_FILTER, &filt, &optlen));
396 ASSERT_EQ(EOPNOTSUPP, errno);
397 close(fd);
398}
399
400TEST_F(raw, bad_optname)
401{
402 socklen_t optlen;
403 int val;
404
405 optlen = sizeof(val);
406
407 ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen));
408 ASSERT_EQ(ENOPROTOOPT, errno);
409 ASSERT_EQ(sizeof(val), optlen);
410}
411
412/* ---------- raw (ipv6) ---------- */
413
414FIXTURE(rawv6)
415{
416 int fd;
417};
418
419FIXTURE_SETUP(rawv6)
420{
421 self->fd = socket(AF_INET6, SOCK_RAW, IPPROTO_UDP);
422 if (self->fd < 0)
423 SKIP(return, "SOCK_RAW/IPv6 socket: %s", strerror(errno));
424}
425
426FIXTURE_TEARDOWN(rawv6)
427{
428 if (self->fd >= 0)
429 close(self->fd);
430}
431
432TEST_F(rawv6, hdrincl_exact)
433{
434 socklen_t optlen;
435 int val = -1;
436
437 optlen = sizeof(val);
438
439 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
440 &val, &optlen));
441 ASSERT_EQ(sizeof(int), optlen);
442 ASSERT_TRUE(val == 0 || val == 1);
443}
444
445TEST_F(rawv6, hdrincl_oversize_clamped)
446{
447 char buf[16] = {};
448 socklen_t optlen = sizeof(buf);
449
450 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
451 buf, &optlen));
452 ASSERT_EQ(sizeof(int), optlen);
453}
454
455/* Raw int options clamp the reported length down to the user buffer
456 * instead of returning EINVAL on a short buffer.
457 */
458TEST_F(rawv6, hdrincl_undersize_clamped)
459{
460 socklen_t optlen = 2;
461 int val = 0;
462
463 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_HDRINCL,
464 &val, &optlen));
465 ASSERT_EQ(2, optlen);
466}
467
468TEST_F(rawv6, checksum_default)
469{
470 socklen_t optlen;
471 int val = 0;
472
473 optlen = sizeof(val);
474
475 /* A non-ICMPv6 raw socket has the checksum disabled, reported as -1. */
476 ASSERT_EQ(0, getsockopt(self->fd, IPPROTO_IPV6, IPV6_CHECKSUM,
477 &val, &optlen));
478 ASSERT_EQ(sizeof(int), optlen);
479 ASSERT_EQ(-1, val);
480}
481
482TEST_F(rawv6, bad_optname)
483{
484 socklen_t optlen;
485 int val;
486
487 optlen = sizeof(val);
488
489 /* SOL_RAW reaches do_rawv6_getsockopt() directly. */
490 ASSERT_EQ(-1, getsockopt(self->fd, SOL_RAW, 0x7fff, &val, &optlen));
491 ASSERT_EQ(ENOPROTOOPT, errno);
492 ASSERT_EQ(sizeof(val), optlen);
493}
494
495/* ---------- tls ---------- */
496
497FIXTURE(tls)
498{
499 int fd;
500 int sfd;
501};
502
503FIXTURE_SETUP(tls)
504{
505 struct sockaddr_in a = {
506 .sin_family = AF_INET,
507 .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
508 };
509 socklen_t alen = sizeof(a);
510 int lfd;
511
512 self->fd = -1;
513 self->sfd = -1;
514
515 lfd = socket(AF_INET, SOCK_STREAM, 0);
516 if (lfd < 0)
517 SKIP(return, "TCP socket: %s", strerror(errno));
518 if (bind(lfd, (struct sockaddr *)&a, sizeof(a)) || listen(lfd, 1) ||
519 getsockname(lfd, (struct sockaddr *)&a, &alen)) {
520 close(lfd);
521 SKIP(return, "listener setup: %s", strerror(errno));
522 }
523 self->fd = socket(AF_INET, SOCK_STREAM, 0);
524 if (self->fd < 0) {
525 close(lfd);
526 SKIP(return, "TCP socket: %s", strerror(errno));
527 }
528 if (connect(self->fd, (struct sockaddr *)&a, sizeof(a))) {
529 close(lfd);
530 SKIP(return, "connect: %s", strerror(errno));
531 }
532 self->sfd = accept(lfd, NULL, NULL);
533 close(lfd);
534 if (setsockopt(self->fd, IPPROTO_TCP, TCP_ULP, "tls", sizeof("tls")))
535 SKIP(return, "TCP_ULP=tls: %s (built without TLS?)",
536 strerror(errno));
537}
538
539FIXTURE_TEARDOWN(tls)
540{
541 if (self->fd >= 0)
542 close(self->fd);
543 if (self->sfd >= 0)
544 close(self->sfd);
545}
546
547/* do_tls_getsockopt_tx_zc(): fixed-size int, exact length required. */
548TEST_F(tls, tx_zerocopy_exact)
549{
550 socklen_t optlen = sizeof(int);
551 int val = -1;
552
553 ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO,
554 &val, &optlen));
555 ASSERT_EQ(sizeof(int), optlen);
556 ASSERT_TRUE(val == 0 || val == 1);
557}
558
559TEST_F(tls, tx_zerocopy_wrong_len)
560{
561 socklen_t optlen = 2;
562 int val;
563
564 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX_ZEROCOPY_RO,
565 &val, &optlen));
566 ASSERT_EQ(EINVAL, errno);
567}
568
569/* do_tls_getsockopt_conf(): NULL optval still yields EINVAL -- the
570 * converted code tests opt->iter_out.ubuf in place of optval.
571 */
572TEST_F(tls, conf_null_optval)
573{
574 socklen_t optlen = 64;
575
576 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, NULL, &optlen));
577 ASSERT_EQ(EINVAL, errno);
578}
579
580TEST_F(tls, conf_short)
581{
582 socklen_t optlen = 2;
583 char buf[2];
584
585 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, buf, &optlen));
586 ASSERT_EQ(EINVAL, errno);
587}
588
589/* TLS_TX before crypto is set reports not-ready. */
590TEST_F(tls, conf_not_ready)
591{
592 struct tls_crypto_info info;
593 socklen_t optlen = sizeof(info);
594
595 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, TLS_TX, &info, &optlen));
596 ASSERT_EQ(EBUSY, errno);
597}
598
599/* Set TX crypto, then read it back at the base and full sizes, exercising
600 * both copy_to_iter() branches. SKIP if AES-GCM is unavailable.
601 */
602TEST_F(tls, conf_crypto_roundtrip)
603{
604 struct tls12_crypto_info_aes_gcm_128 tx = {
605 .info.version = TLS_1_2_VERSION,
606 .info.cipher_type = TLS_CIPHER_AES_GCM_128,
607 };
608 struct tls12_crypto_info_aes_gcm_128 full;
609 struct tls_crypto_info base;
610 socklen_t optlen;
611
612 if (setsockopt(self->fd, SOL_TLS, TLS_TX, &tx, sizeof(tx)))
613 SKIP(return, "set TLS_TX aes_gcm_128: %s", strerror(errno));
614
615 optlen = sizeof(base);
616 ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &base, &optlen));
617 ASSERT_EQ(sizeof(base), optlen);
618 ASSERT_EQ(TLS_1_2_VERSION, base.version);
619 ASSERT_EQ(TLS_CIPHER_AES_GCM_128, base.cipher_type);
620
621 optlen = sizeof(full);
622 ASSERT_EQ(0, getsockopt(self->fd, SOL_TLS, TLS_TX, &full, &optlen));
623 ASSERT_EQ(sizeof(full), optlen);
624 ASSERT_EQ(TLS_CIPHER_AES_GCM_128, full.info.cipher_type);
625}
626
627TEST_F(tls, bad_optname)
628{
629 socklen_t optlen = sizeof(int);
630 int val;
631
632 ASSERT_EQ(-1, getsockopt(self->fd, SOL_TLS, 0x7fff, &val, &optlen));
633 ASSERT_EQ(ENOPROTOOPT, errno);
634}
635
636TEST_HARNESS_MAIN