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 <leitao@debian.org>
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
FIXTURE(netlink)61 FIXTURE(netlink)
62 {
63 int fd;
64 };
65
FIXTURE_SETUP(netlink)66 FIXTURE_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
FIXTURE_TEARDOWN(netlink)82 FIXTURE_TEARDOWN(netlink)
83 {
84 if (self->fd >= 0)
85 close(self->fd);
86 }
87
TEST_F(netlink,pktinfo_exact)88 TEST_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
TEST_F(netlink,pktinfo_oversize_clamped)101 TEST_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
TEST_F(netlink,pktinfo_undersize)113 TEST_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
TEST_F(netlink,list_memberships_size_discovery)126 TEST_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
TEST_F(netlink,list_memberships_full_read)138 TEST_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
TEST_F(netlink,bad_level)153 TEST_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
TEST_F(netlink,bad_optname)166 TEST_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
FIXTURE(vsock)181 FIXTURE(vsock)
182 {
183 int fd;
184 };
185
FIXTURE_SETUP(vsock)186 FIXTURE_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
FIXTURE_TEARDOWN(vsock)193 FIXTURE_TEARDOWN(vsock)
194 {
195 if (self->fd >= 0)
196 close(self->fd);
197 }
198
TEST_F(vsock,buffer_size_exact)199 TEST_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
TEST_F(vsock,buffer_size_oversize_clamped)213 TEST_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
TEST_F(vsock,buffer_size_undersize)226 TEST_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
TEST_F(vsock,bad_level)240 TEST_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
TEST_F(vsock,bad_optname)254 TEST_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 */
TEST_F(vsock,connect_timeout_new_exact)272 TEST_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
TEST_F(vsock,connect_timeout_new_oversize_clamped)285 TEST_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
TEST_F(vsock,connect_timeout_new_undersize)298 TEST_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
TEST_F(vsock,connect_timeout_old_exact)312 TEST_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
FIXTURE(raw)327 FIXTURE(raw)
328 {
329 int fd;
330 };
331
FIXTURE_SETUP(raw)332 FIXTURE_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
FIXTURE_TEARDOWN(raw)344 FIXTURE_TEARDOWN(raw)
345 {
346 if (self->fd >= 0)
347 close(self->fd);
348 }
349
TEST_F(raw,icmpfilter_exact)350 TEST_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
TEST_F(raw,icmpfilter_oversize_clamped)361 TEST_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 */
TEST_F(raw,icmpfilter_undersize_clamped)375 TEST_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
TEST_F(raw,icmpfilter_wrong_proto)385 TEST_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
TEST_F(raw,bad_optname)400 TEST_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
FIXTURE(rawv6)414 FIXTURE(rawv6)
415 {
416 int fd;
417 };
418
FIXTURE_SETUP(rawv6)419 FIXTURE_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
FIXTURE_TEARDOWN(rawv6)426 FIXTURE_TEARDOWN(rawv6)
427 {
428 if (self->fd >= 0)
429 close(self->fd);
430 }
431
TEST_F(rawv6,hdrincl_exact)432 TEST_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
TEST_F(rawv6,hdrincl_oversize_clamped)445 TEST_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 */
TEST_F(rawv6,hdrincl_undersize_clamped)458 TEST_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
TEST_F(rawv6,checksum_default)468 TEST_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
TEST_F(rawv6,bad_optname)482 TEST_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
FIXTURE(tls)497 FIXTURE(tls)
498 {
499 int fd;
500 int sfd;
501 };
502
FIXTURE_SETUP(tls)503 FIXTURE_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
FIXTURE_TEARDOWN(tls)539 FIXTURE_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. */
TEST_F(tls,tx_zerocopy_exact)548 TEST_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
TEST_F(tls,tx_zerocopy_wrong_len)559 TEST_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 */
TEST_F(tls,conf_null_optval)572 TEST_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
TEST_F(tls,conf_short)580 TEST_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. */
TEST_F(tls,conf_not_ready)590 TEST_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 */
TEST_F(tls,conf_crypto_roundtrip)602 TEST_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
TEST_F(tls,bad_optname)627 TEST_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
636 TEST_HARNESS_MAIN
637