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
afstr(int af,int proto)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
sk_peek_offset_probe(sa_family_t af,int proto)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
sk_peek_offset_set(int s,int offset)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
sk_peek_offset_get(int s)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
sk_peek_offset_test(sa_family_t af,int proto)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
do_test(int proto)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
main(void)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