xref: /linux/tools/testing/selftests/net/getsockopt_iter.c (revision db30e412b7f543d00396ab27f690608cad06aa97)
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  *
15  * Author: Breno Leitao <leitao@debian.org>
16  */
17 
18 #include <errno.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <linux/netlink.h>
24 #include <linux/rtnetlink.h>
25 #include <linux/time_types.h>
26 #include <linux/vm_sockets.h>
27 #include <sys/socket.h>
28 #include "kselftest_harness.h"
29 
30 #ifndef AF_VSOCK
31 #define AF_VSOCK 40
32 #endif
33 
34 /* ---------- netlink ---------- */
35 
36 FIXTURE(netlink)
37 {
38 	int fd;
39 };
40 
41 FIXTURE_SETUP(netlink)
42 {
43 	int group = RTNLGRP_LINK;
44 
45 	self->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
46 	if (self->fd < 0)
47 		SKIP(return, "AF_NETLINK socket: %s", strerror(errno));
48 
49 	/* Joining a multicast group grows nlk->ngroups so the
50 	 * NETLINK_LIST_MEMBERSHIPS path has a non-zero size to report.
51 	 */
52 	if (setsockopt(self->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
53 		       &group, sizeof(group)) < 0)
54 		SKIP(return, "NETLINK_ADD_MEMBERSHIP: %s", strerror(errno));
55 }
56 
57 FIXTURE_TEARDOWN(netlink)
58 {
59 	if (self->fd >= 0)
60 		close(self->fd);
61 }
62 
63 TEST_F(netlink, pktinfo_exact)
64 {
65 	socklen_t optlen;
66 	int val = -1;
67 
68 	optlen = sizeof(val);
69 
70 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
71 				&val, &optlen));
72 	ASSERT_EQ(sizeof(int), optlen);
73 	ASSERT_TRUE(val == 0 || val == 1);
74 }
75 
76 TEST_F(netlink, pktinfo_oversize_clamped)
77 {
78 	char buf[16] = {};
79 	socklen_t optlen;
80 
81 	optlen = sizeof(buf);
82 
83 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
84 				buf, &optlen));
85 	ASSERT_EQ(sizeof(int), optlen);
86 }
87 
88 TEST_F(netlink, pktinfo_undersize)
89 {
90 	char buf[2] = {};
91 	socklen_t optlen;
92 
93 	optlen = sizeof(buf);
94 
95 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
96 				 buf, &optlen));
97 	ASSERT_EQ(EINVAL, errno);
98 	ASSERT_EQ(sizeof(buf), optlen);
99 }
100 
101 TEST_F(netlink, list_memberships_size_discovery)
102 {
103 	socklen_t optlen = 0;
104 	char dummy;
105 
106 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK,
107 				NETLINK_LIST_MEMBERSHIPS,
108 				&dummy, &optlen));
109 	ASSERT_GT(optlen, 0);
110 	ASSERT_EQ(0, optlen % sizeof(__u32));
111 }
112 
113 TEST_F(netlink, list_memberships_full_read)
114 {
115 	__u32 buf[64] = {};
116 	socklen_t optlen;
117 
118 	optlen = sizeof(buf);
119 
120 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK,
121 				NETLINK_LIST_MEMBERSHIPS,
122 				buf, &optlen));
123 	ASSERT_GT(optlen, 0);
124 	ASSERT_LE(optlen, sizeof(buf));
125 	ASSERT_EQ(0, optlen % sizeof(__u32));
126 }
127 
128 TEST_F(netlink, bad_level)
129 {
130 	socklen_t optlen;
131 	int val;
132 
133 	optlen = sizeof(val);
134 
135 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, NETLINK_PKTINFO,
136 				 &val, &optlen));
137 	ASSERT_EQ(ENOPROTOOPT, errno);
138 	ASSERT_EQ(sizeof(val), optlen);
139 }
140 
141 TEST_F(netlink, bad_optname)
142 {
143 	socklen_t optlen;
144 	int val;
145 
146 	optlen = sizeof(val);
147 
148 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, 0x7fff,
149 				 &val, &optlen));
150 	ASSERT_EQ(ENOPROTOOPT, errno);
151 	ASSERT_EQ(sizeof(val), optlen);
152 }
153 
154 /* ---------- vsock ---------- */
155 
156 FIXTURE(vsock)
157 {
158 	int fd;
159 };
160 
161 FIXTURE_SETUP(vsock)
162 {
163 	self->fd = socket(AF_VSOCK, SOCK_STREAM, 0);
164 	if (self->fd < 0)
165 		SKIP(return, "AF_VSOCK socket: %s", strerror(errno));
166 }
167 
168 FIXTURE_TEARDOWN(vsock)
169 {
170 	if (self->fd >= 0)
171 		close(self->fd);
172 }
173 
174 TEST_F(vsock, buffer_size_exact)
175 {
176 	socklen_t optlen;
177 	uint64_t val = 0;
178 
179 	optlen = sizeof(val);
180 
181 	ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
182 				SO_VM_SOCKETS_BUFFER_SIZE,
183 				&val, &optlen));
184 	ASSERT_EQ(sizeof(uint64_t), optlen);
185 	ASSERT_GT(val, 0);
186 }
187 
188 TEST_F(vsock, buffer_size_oversize_clamped)
189 {
190 	char buf[16] = {};
191 	socklen_t optlen;
192 
193 	optlen = sizeof(buf);
194 
195 	ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
196 				SO_VM_SOCKETS_BUFFER_SIZE,
197 				buf, &optlen));
198 	ASSERT_EQ(sizeof(uint64_t), optlen);
199 }
200 
201 TEST_F(vsock, buffer_size_undersize)
202 {
203 	char buf[4] = {};
204 	socklen_t optlen;
205 
206 	optlen = sizeof(buf);
207 
208 	ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK,
209 				 SO_VM_SOCKETS_BUFFER_SIZE,
210 				 buf, &optlen));
211 	ASSERT_EQ(EINVAL, errno);
212 	ASSERT_EQ(sizeof(buf), optlen);
213 }
214 
215 TEST_F(vsock, bad_level)
216 {
217 	socklen_t optlen;
218 	uint64_t val;
219 
220 	optlen = sizeof(val);
221 
222 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1,
223 				 SO_VM_SOCKETS_BUFFER_SIZE,
224 				 &val, &optlen));
225 	ASSERT_EQ(ENOPROTOOPT, errno);
226 	ASSERT_EQ(sizeof(val), optlen);
227 }
228 
229 TEST_F(vsock, bad_optname)
230 {
231 	socklen_t optlen;
232 	uint64_t val;
233 
234 	optlen = sizeof(val);
235 
236 	ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 0x7fff,
237 				 &val, &optlen));
238 	ASSERT_EQ(ENOPROTOOPT, errno);
239 	ASSERT_EQ(sizeof(val), optlen);
240 }
241 
242 /* SO_VM_SOCKETS_CONNECT_TIMEOUT_{NEW,OLD} return a sock_timeval-shaped
243  * payload, which is wider than u64 on 64-bit. They exercise the path
244  * where the protocol's reported lv (16 bytes) is larger than the
245  * common 8-byte u64 case covered above.
246  */
247 TEST_F(vsock, connect_timeout_new_exact)
248 {
249 	struct __kernel_sock_timeval tv = {};
250 	socklen_t optlen;
251 
252 	optlen = sizeof(tv);
253 
254 	ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
255 				SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
256 				&tv, &optlen));
257 	ASSERT_EQ(sizeof(tv), optlen);
258 }
259 
260 TEST_F(vsock, connect_timeout_new_oversize_clamped)
261 {
262 	char buf[sizeof(struct __kernel_sock_timeval) * 2] = {};
263 	socklen_t optlen;
264 
265 	optlen = sizeof(buf);
266 
267 	ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
268 				SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
269 				buf, &optlen));
270 	ASSERT_EQ(sizeof(struct __kernel_sock_timeval), optlen);
271 }
272 
273 TEST_F(vsock, connect_timeout_new_undersize)
274 {
275 	socklen_t optlen;
276 	uint64_t val;
277 
278 	optlen = sizeof(val);
279 
280 	ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK,
281 				 SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW,
282 				 &val, &optlen));
283 	ASSERT_EQ(EINVAL, errno);
284 	ASSERT_EQ(sizeof(val), optlen);
285 }
286 
287 TEST_F(vsock, connect_timeout_old_exact)
288 {
289 	struct __kernel_old_timeval tv = {};
290 	socklen_t optlen;
291 
292 	optlen = sizeof(tv);
293 
294 	ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
295 				SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD,
296 				&tv, &optlen));
297 	ASSERT_EQ(sizeof(tv), optlen);
298 }
299 
300 TEST_HARNESS_MAIN
301