1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright 2025 Google LLC */
3
4 #include <linux/sockios.h>
5 #include <sys/ioctl.h>
6 #include <sys/socket.h>
7 #include <sys/types.h>
8
9 #include "../../kselftest_harness.h"
10
11 #define NR_CHUNKS 100
12 #define MSG_LEN 256
13
14 struct scm_inq {
15 struct cmsghdr cmsghdr;
16 int inq;
17 };
18
FIXTURE(scm_inq)19 FIXTURE(scm_inq)
20 {
21 int fd[2];
22 };
23
FIXTURE_VARIANT(scm_inq)24 FIXTURE_VARIANT(scm_inq)
25 {
26 int type;
27 };
28
FIXTURE_VARIANT_ADD(scm_inq,stream)29 FIXTURE_VARIANT_ADD(scm_inq, stream)
30 {
31 .type = SOCK_STREAM,
32 };
33
FIXTURE_VARIANT_ADD(scm_inq,dgram)34 FIXTURE_VARIANT_ADD(scm_inq, dgram)
35 {
36 .type = SOCK_DGRAM,
37 };
38
FIXTURE_VARIANT_ADD(scm_inq,seqpacket)39 FIXTURE_VARIANT_ADD(scm_inq, seqpacket)
40 {
41 .type = SOCK_SEQPACKET,
42 };
43
FIXTURE_SETUP(scm_inq)44 FIXTURE_SETUP(scm_inq)
45 {
46 int err;
47
48 err = socketpair(AF_UNIX, variant->type | SOCK_NONBLOCK, 0, self->fd);
49 ASSERT_EQ(0, err);
50 }
51
FIXTURE_TEARDOWN(scm_inq)52 FIXTURE_TEARDOWN(scm_inq)
53 {
54 close(self->fd[0]);
55 close(self->fd[1]);
56 }
57
send_chunks(struct __test_metadata * _metadata,FIXTURE_DATA (scm_inq)* self)58 static void send_chunks(struct __test_metadata *_metadata,
59 FIXTURE_DATA(scm_inq) *self)
60 {
61 char buf[MSG_LEN] = {};
62 int i, ret;
63
64 for (i = 0; i < NR_CHUNKS; i++) {
65 ret = send(self->fd[0], buf, sizeof(buf), 0);
66 ASSERT_EQ(sizeof(buf), ret);
67 }
68 }
69
recv_chunks(struct __test_metadata * _metadata,FIXTURE_DATA (scm_inq)* self)70 static void recv_chunks(struct __test_metadata *_metadata,
71 FIXTURE_DATA(scm_inq) *self)
72 {
73 struct msghdr msg = {};
74 struct iovec iov = {};
75 struct scm_inq cmsg;
76 char buf[MSG_LEN];
77 int i, ret;
78 int inq;
79
80 msg.msg_iov = &iov;
81 msg.msg_iovlen = 1;
82 msg.msg_control = &cmsg;
83 msg.msg_controllen = CMSG_SPACE(sizeof(cmsg.inq));
84
85 iov.iov_base = buf;
86 iov.iov_len = sizeof(buf);
87
88 for (i = 0; i < NR_CHUNKS; i++) {
89 memset(buf, 0, sizeof(buf));
90 memset(&cmsg, 0, sizeof(cmsg));
91
92 ret = recvmsg(self->fd[1], &msg, 0);
93 ASSERT_EQ(MSG_LEN, ret);
94 ASSERT_NE(NULL, CMSG_FIRSTHDR(&msg));
95 ASSERT_EQ(CMSG_LEN(sizeof(cmsg.inq)), cmsg.cmsghdr.cmsg_len);
96 ASSERT_EQ(SOL_SOCKET, cmsg.cmsghdr.cmsg_level);
97 ASSERT_EQ(SCM_INQ, cmsg.cmsghdr.cmsg_type);
98
99 ret = ioctl(self->fd[1], SIOCINQ, &inq);
100 ASSERT_EQ(0, ret);
101 ASSERT_EQ(cmsg.inq, inq);
102 }
103 }
104
TEST_F(scm_inq,basic)105 TEST_F(scm_inq, basic)
106 {
107 int err, inq;
108
109 err = setsockopt(self->fd[1], SOL_SOCKET, SO_INQ, &(int){1}, sizeof(int));
110 if (variant->type != SOCK_STREAM) {
111 ASSERT_EQ(-ENOPROTOOPT, -errno);
112 return;
113 }
114
115 ASSERT_EQ(0, err);
116
117 err = ioctl(self->fd[1], SIOCINQ, &inq);
118 ASSERT_EQ(0, err);
119 ASSERT_EQ(0, inq);
120
121 send_chunks(_metadata, self);
122 recv_chunks(_metadata, self);
123 }
124
125 TEST_HARNESS_MAIN
126