1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <errno.h> 8 #include <sys/types.h> 9 #include <netinet/in.h> 10 #include <arpa/inet.h> 11 #include "../kselftest.h" 12 13 static char *afstr(int af, int proto) 14 { 15 if (proto == IPPROTO_TCP) 16 return af == AF_INET ? "TCP/IPv4" : "TCP/IPv6"; 17 else 18 return af == AF_INET ? "UDP/IPv4" : "UDP/IPv6"; 19 } 20 21 int sk_peek_offset_probe(sa_family_t af, int proto) 22 { 23 int type = (proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM); 24 int optv = 0; 25 int ret = 0; 26 int s; 27 28 s = socket(af, type, proto); 29 if (s < 0) { 30 ksft_perror("Temporary TCP socket creation failed"); 31 } else { 32 if (!setsockopt(s, SOL_SOCKET, SO_PEEK_OFF, &optv, sizeof(int))) 33 ret = 1; 34 else 35 printf("%s does not support SO_PEEK_OFF\n", afstr(af, proto)); 36 close(s); 37 } 38 return ret; 39 } 40 41 static void sk_peek_offset_set(int s, int offset) 42 { 43 if (setsockopt(s, SOL_SOCKET, SO_PEEK_OFF, &offset, sizeof(offset))) 44 ksft_perror("Failed to set SO_PEEK_OFF value\n"); 45 } 46 47 static int sk_peek_offset_get(int s) 48 { 49 int offset; 50 socklen_t len = sizeof(offset); 51 52 if (getsockopt(s, SOL_SOCKET, SO_PEEK_OFF, &offset, &len)) 53 ksft_perror("Failed to get SO_PEEK_OFF value\n"); 54 return offset; 55 } 56 57 static int sk_peek_offset_test(sa_family_t af, int proto) 58 { 59 int type = (proto == IPPROTO_TCP ? SOCK_STREAM : SOCK_DGRAM); 60 union { 61 struct sockaddr sa; 62 struct sockaddr_in a4; 63 struct sockaddr_in6 a6; 64 } a; 65 int res = 0; 66 int s[2] = {0, 0}; 67 int recv_sock = 0; 68 int offset = 0; 69 ssize_t len; 70 char buf[2]; 71 72 memset(&a, 0, sizeof(a)); 73 a.sa.sa_family = af; 74 75 s[0] = recv_sock = socket(af, type, proto); 76 s[1] = socket(af, type, proto); 77 78 if (s[0] < 0 || s[1] < 0) { 79 ksft_perror("Temporary socket creation failed\n"); 80 goto out; 81 } 82 if (bind(s[0], &a.sa, sizeof(a)) < 0) { 83 ksft_perror("Temporary socket bind() failed\n"); 84 goto out; 85 } 86 if (getsockname(s[0], &a.sa, &((socklen_t) { sizeof(a) })) < 0) { 87 ksft_perror("Temporary socket getsockname() failed\n"); 88 goto out; 89 } 90 if (proto == IPPROTO_TCP && listen(s[0], 0) < 0) { 91 ksft_perror("Temporary socket listen() failed\n"); 92 goto out; 93 } 94 if (connect(s[1], &a.sa, sizeof(a)) < 0) { 95 ksft_perror("Temporary socket connect() failed\n"); 96 goto out; 97 } 98 if (proto == IPPROTO_TCP) { 99 recv_sock = accept(s[0], NULL, NULL); 100 if (recv_sock <= 0) { 101 ksft_perror("Temporary socket accept() failed\n"); 102 goto out; 103 } 104 } 105 106 /* Some basic tests of getting/setting offset */ 107 offset = sk_peek_offset_get(recv_sock); 108 if (offset != -1) { 109 ksft_perror("Initial value of socket offset not -1\n"); 110 goto out; 111 } 112 sk_peek_offset_set(recv_sock, 0); 113 offset = sk_peek_offset_get(recv_sock); 114 if (offset != 0) { 115 ksft_perror("Failed to set socket offset to 0\n"); 116 goto out; 117 } 118 119 /* Transfer a message */ 120 if (send(s[1], (char *)("ab"), 2, 0) != 2) { 121 ksft_perror("Temporary probe socket send() failed\n"); 122 goto out; 123 } 124 /* Read first byte */ 125 len = recv(recv_sock, buf, 1, MSG_PEEK); 126 if (len != 1 || buf[0] != 'a') { 127 ksft_perror("Failed to read first byte of message\n"); 128 goto out; 129 } 130 offset = sk_peek_offset_get(recv_sock); 131 if (offset != 1) { 132 ksft_perror("Offset not forwarded correctly at first byte\n"); 133 goto out; 134 } 135 /* Try to read beyond last byte */ 136 len = recv(recv_sock, buf, 2, MSG_PEEK); 137 if (len != 1 || buf[0] != 'b') { 138 ksft_perror("Failed to read last byte of message\n"); 139 goto out; 140 } 141 offset = sk_peek_offset_get(recv_sock); 142 if (offset != 2) { 143 ksft_perror("Offset not forwarded correctly at last byte\n"); 144 goto out; 145 } 146 /* Flush message */ 147 len = recv(recv_sock, buf, 2, MSG_TRUNC); 148 if (len != 2) { 149 ksft_perror("Failed to flush message\n"); 150 goto out; 151 } 152 offset = sk_peek_offset_get(recv_sock); 153 if (offset != 0) { 154 ksft_perror("Offset not reverted correctly after flush\n"); 155 goto out; 156 } 157 158 printf("%s with MSG_PEEK_OFF works correctly\n", afstr(af, proto)); 159 res = 1; 160 out: 161 if (proto == IPPROTO_TCP && recv_sock >= 0) 162 close(recv_sock); 163 if (s[1] >= 0) 164 close(s[1]); 165 if (s[0] >= 0) 166 close(s[0]); 167 return res; 168 } 169 170 static int do_test(int proto) 171 { 172 int res4, res6; 173 174 res4 = sk_peek_offset_probe(AF_INET, proto); 175 res6 = sk_peek_offset_probe(AF_INET6, proto); 176 177 if (!res4 && !res6) 178 return KSFT_SKIP; 179 180 if (res4) 181 res4 = sk_peek_offset_test(AF_INET, proto); 182 183 if (res6) 184 res6 = sk_peek_offset_test(AF_INET6, proto); 185 186 if (!res4 || !res6) 187 return KSFT_FAIL; 188 189 return KSFT_PASS; 190 } 191 192 int main(void) 193 { 194 int restcp, resudp; 195 196 restcp = do_test(IPPROTO_TCP); 197 resudp = do_test(IPPROTO_UDP); 198 if (restcp == KSFT_FAIL || resudp == KSFT_FAIL) 199 return KSFT_FAIL; 200 201 return KSFT_PASS; 202 } 203