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