xref: /linux/tools/testing/selftests/net/af_unix/scm_inq.c (revision ddd664bbff63e09e7a7f9acae9c43605d4cf185f)
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