xref: /linux/tools/testing/selftests/net/getsockopt_iter.c (revision 6a4c4656b0d2d4056a1f0c35442db4e8a5cf8021)
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/vm_sockets.h>
26 #include <sys/socket.h>
27 #include "kselftest_harness.h"
28 
29 #ifndef AF_VSOCK
30 #define AF_VSOCK 40
31 #endif
32 
33 /* ---------- netlink ---------- */
34 
35 FIXTURE(netlink)
36 {
37 	int fd;
38 };
39 
40 FIXTURE_SETUP(netlink)
41 {
42 	int group = RTNLGRP_LINK;
43 
44 	self->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
45 	if (self->fd < 0)
46 		SKIP(return, "AF_NETLINK socket: %s", strerror(errno));
47 
48 	/* Joining a multicast group grows nlk->ngroups so the
49 	 * NETLINK_LIST_MEMBERSHIPS path has a non-zero size to report.
50 	 */
51 	if (setsockopt(self->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
52 		       &group, sizeof(group)) < 0)
53 		SKIP(return, "NETLINK_ADD_MEMBERSHIP: %s", strerror(errno));
54 }
55 
56 FIXTURE_TEARDOWN(netlink)
57 {
58 	if (self->fd >= 0)
59 		close(self->fd);
60 }
61 
62 TEST_F(netlink, pktinfo_exact)
63 {
64 	int val = -1;
65 	socklen_t optlen = sizeof(val);
66 
67 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
68 				&val, &optlen));
69 	ASSERT_EQ(sizeof(int), optlen);
70 	ASSERT_TRUE(val == 0 || val == 1);
71 }
72 
73 TEST_F(netlink, pktinfo_oversize_clamped)
74 {
75 	char buf[16] = {};
76 	socklen_t optlen = sizeof(buf);
77 
78 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
79 				buf, &optlen));
80 	ASSERT_EQ(sizeof(int), optlen);
81 }
82 
83 TEST_F(netlink, pktinfo_undersize)
84 {
85 	char buf[2] = {};
86 	socklen_t optlen = sizeof(buf);
87 
88 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, NETLINK_PKTINFO,
89 				 buf, &optlen));
90 	ASSERT_EQ(EINVAL, errno);
91 }
92 
93 TEST_F(netlink, list_memberships_size_discovery)
94 {
95 	socklen_t optlen = 0;
96 	char dummy;
97 
98 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK,
99 				NETLINK_LIST_MEMBERSHIPS,
100 				&dummy, &optlen));
101 	ASSERT_GT(optlen, 0);
102 	ASSERT_EQ(0, optlen % sizeof(__u32));
103 }
104 
105 TEST_F(netlink, list_memberships_full_read)
106 {
107 	__u32 buf[64] = {};
108 	socklen_t optlen = sizeof(buf);
109 
110 	ASSERT_EQ(0, getsockopt(self->fd, SOL_NETLINK,
111 				NETLINK_LIST_MEMBERSHIPS,
112 				buf, &optlen));
113 	ASSERT_GT(optlen, 0);
114 	ASSERT_LE(optlen, sizeof(buf));
115 	ASSERT_EQ(0, optlen % sizeof(__u32));
116 }
117 
118 TEST_F(netlink, bad_level)
119 {
120 	int val;
121 	socklen_t optlen = sizeof(val);
122 
123 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1, NETLINK_PKTINFO,
124 				 &val, &optlen));
125 	ASSERT_EQ(ENOPROTOOPT, errno);
126 }
127 
128 TEST_F(netlink, bad_optname)
129 {
130 	int val;
131 	socklen_t optlen = sizeof(val);
132 
133 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_NETLINK, 0x7fff,
134 				 &val, &optlen));
135 	ASSERT_EQ(ENOPROTOOPT, errno);
136 }
137 
138 /* ---------- vsock ---------- */
139 
140 FIXTURE(vsock)
141 {
142 	int fd;
143 };
144 
145 FIXTURE_SETUP(vsock)
146 {
147 	self->fd = socket(AF_VSOCK, SOCK_STREAM, 0);
148 	if (self->fd < 0)
149 		SKIP(return, "AF_VSOCK socket: %s", strerror(errno));
150 }
151 
152 FIXTURE_TEARDOWN(vsock)
153 {
154 	if (self->fd >= 0)
155 		close(self->fd);
156 }
157 
158 TEST_F(vsock, buffer_size_exact)
159 {
160 	uint64_t val = 0;
161 	socklen_t optlen = sizeof(val);
162 
163 	ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
164 				SO_VM_SOCKETS_BUFFER_SIZE,
165 				&val, &optlen));
166 	ASSERT_EQ(sizeof(uint64_t), optlen);
167 	ASSERT_GT(val, 0);
168 }
169 
170 TEST_F(vsock, buffer_size_oversize_clamped)
171 {
172 	char buf[16] = {};
173 	socklen_t optlen = sizeof(buf);
174 
175 	ASSERT_EQ(0, getsockopt(self->fd, AF_VSOCK,
176 				SO_VM_SOCKETS_BUFFER_SIZE,
177 				buf, &optlen));
178 	ASSERT_EQ(sizeof(uint64_t), optlen);
179 }
180 
181 TEST_F(vsock, buffer_size_undersize)
182 {
183 	char buf[4] = {};
184 	socklen_t optlen = sizeof(buf);
185 
186 	ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK,
187 				 SO_VM_SOCKETS_BUFFER_SIZE,
188 				 buf, &optlen));
189 	ASSERT_EQ(EINVAL, errno);
190 }
191 
192 TEST_F(vsock, bad_level)
193 {
194 	uint64_t val;
195 	socklen_t optlen = sizeof(val);
196 
197 	ASSERT_EQ(-1, getsockopt(self->fd, SOL_SOCKET + 1,
198 				 SO_VM_SOCKETS_BUFFER_SIZE,
199 				 &val, &optlen));
200 	ASSERT_EQ(ENOPROTOOPT, errno);
201 }
202 
203 TEST_F(vsock, bad_optname)
204 {
205 	uint64_t val;
206 	socklen_t optlen = sizeof(val);
207 
208 	ASSERT_EQ(-1, getsockopt(self->fd, AF_VSOCK, 0x7fff,
209 				 &val, &optlen));
210 	ASSERT_EQ(ENOPROTOOPT, errno);
211 }
212 
213 TEST_HARNESS_MAIN
214