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