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 #define NR_PARTIAL_READS 3
14
FIXTURE(scm_inq)15 FIXTURE(scm_inq)
16 {
17 int fd[2];
18 };
19
FIXTURE_VARIANT(scm_inq)20 FIXTURE_VARIANT(scm_inq)
21 {
22 int type;
23 };
24
FIXTURE_VARIANT_ADD(scm_inq,stream)25 FIXTURE_VARIANT_ADD(scm_inq, stream)
26 {
27 .type = SOCK_STREAM,
28 };
29
FIXTURE_VARIANT_ADD(scm_inq,dgram)30 FIXTURE_VARIANT_ADD(scm_inq, dgram)
31 {
32 .type = SOCK_DGRAM,
33 };
34
FIXTURE_VARIANT_ADD(scm_inq,seqpacket)35 FIXTURE_VARIANT_ADD(scm_inq, seqpacket)
36 {
37 .type = SOCK_SEQPACKET,
38 };
39
FIXTURE_SETUP(scm_inq)40 FIXTURE_SETUP(scm_inq)
41 {
42 int err;
43
44 err = socketpair(AF_UNIX, variant->type | SOCK_NONBLOCK, 0, self->fd);
45 ASSERT_EQ(0, err);
46 }
47
FIXTURE_TEARDOWN(scm_inq)48 FIXTURE_TEARDOWN(scm_inq)
49 {
50 close(self->fd[0]);
51 close(self->fd[1]);
52 }
53
send_chunks(struct __test_metadata * _metadata,FIXTURE_DATA (scm_inq)* self)54 static void send_chunks(struct __test_metadata *_metadata,
55 FIXTURE_DATA(scm_inq) *self)
56 {
57 char buf[MSG_LEN] = {};
58 int i, ret;
59
60 for (i = 0; i < NR_CHUNKS; i++) {
61 ret = send(self->fd[0], buf, sizeof(buf), 0);
62 ASSERT_EQ(sizeof(buf), ret);
63 }
64 }
65
recv_chunks(struct __test_metadata * _metadata,FIXTURE_DATA (scm_inq)* self)66 static void recv_chunks(struct __test_metadata *_metadata,
67 FIXTURE_DATA(scm_inq) *self)
68 {
69 char cmsg_buf[CMSG_SPACE(sizeof(int))];
70 struct msghdr msg = {};
71 struct iovec iov = {};
72 struct cmsghdr *cmsg;
73 char buf[MSG_LEN];
74 int i, ret;
75 int inq;
76
77 msg.msg_iov = &iov;
78 msg.msg_iovlen = 1;
79 msg.msg_control = cmsg_buf;
80 msg.msg_controllen = sizeof(cmsg_buf);
81
82 iov.iov_base = buf;
83 iov.iov_len = sizeof(buf);
84
85 for (i = 0; i < NR_CHUNKS; i++) {
86 memset(buf, 0, sizeof(buf));
87 memset(cmsg_buf, 0, sizeof(cmsg_buf));
88
89 ret = recvmsg(self->fd[1], &msg, 0);
90 ASSERT_EQ(MSG_LEN, ret);
91
92 cmsg = CMSG_FIRSTHDR(&msg);
93 ASSERT_NE(NULL, cmsg);
94 ASSERT_EQ(CMSG_LEN(sizeof(int)), cmsg->cmsg_len);
95 ASSERT_EQ(SOL_SOCKET, cmsg->cmsg_level);
96 ASSERT_EQ(SCM_INQ, cmsg->cmsg_type);
97
98 ret = ioctl(self->fd[1], SIOCINQ, &inq);
99 ASSERT_EQ(0, ret);
100 ASSERT_EQ(*(int *)CMSG_DATA(cmsg), inq);
101 }
102 }
103
TEST_F(scm_inq,basic)104 TEST_F(scm_inq, basic)
105 {
106 int err, inq;
107
108 err = setsockopt(self->fd[1], SOL_SOCKET, SO_INQ, &(int){1}, sizeof(int));
109 if (variant->type != SOCK_STREAM) {
110 ASSERT_EQ(-ENOPROTOOPT, -errno);
111 return;
112 }
113
114 ASSERT_EQ(0, err);
115
116 err = ioctl(self->fd[1], SIOCINQ, &inq);
117 ASSERT_EQ(0, err);
118 ASSERT_EQ(0, inq);
119
120 send_chunks(_metadata, self);
121 recv_chunks(_metadata, self);
122 }
123
TEST_F(scm_inq,partial_read)124 TEST_F(scm_inq, partial_read)
125 {
126 char buf[MSG_LEN * NR_PARTIAL_READS] = {};
127 char cmsg_buf[CMSG_SPACE(sizeof(int))];
128 struct msghdr msg = {};
129 struct iovec iov = {};
130 struct cmsghdr *cmsg;
131 int err, inq, ret, i;
132 int remain;
133
134 err = setsockopt(self->fd[1], SOL_SOCKET, SO_INQ, &(int){1}, sizeof(int));
135 if (variant->type != SOCK_STREAM) {
136 ASSERT_EQ(-ENOPROTOOPT, -errno);
137 return;
138 }
139 ASSERT_EQ(0, err);
140
141 ret = send(self->fd[0], buf, sizeof(buf), 0);
142 ASSERT_EQ(sizeof(buf), ret);
143
144 msg.msg_iov = &iov;
145 msg.msg_iovlen = 1;
146 msg.msg_control = cmsg_buf;
147 msg.msg_controllen = sizeof(cmsg_buf);
148
149 iov.iov_base = buf;
150 iov.iov_len = MSG_LEN;
151
152 for (i = 0; i < NR_PARTIAL_READS; i++) {
153 remain = MSG_LEN * (NR_PARTIAL_READS - 1 - i);
154
155 memset(buf, 0, MSG_LEN);
156 memset(cmsg_buf, 0, sizeof(cmsg_buf));
157 ret = recvmsg(self->fd[1], &msg, 0);
158 ASSERT_EQ(MSG_LEN, ret);
159
160 cmsg = CMSG_FIRSTHDR(&msg);
161 ASSERT_NE(NULL, cmsg);
162 ASSERT_EQ(CMSG_LEN(sizeof(int)), cmsg->cmsg_len);
163 ASSERT_EQ(SOL_SOCKET, cmsg->cmsg_level);
164 ASSERT_EQ(SCM_INQ, cmsg->cmsg_type);
165 ASSERT_EQ(remain, *(int *)CMSG_DATA(cmsg));
166
167 ret = ioctl(self->fd[1], SIOCINQ, &inq);
168 ASSERT_EQ(0, ret);
169 ASSERT_EQ(remain, inq);
170 }
171 }
172
173 TEST_HARNESS_MAIN
174