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