203 lines · c
1// SPDX-License-Identifier: GPL-2.02 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 13static char *afstr(int af, int proto)14{15 if (proto == IPPROTO_TCP)16 return af == AF_INET ? "TCP/IPv4" : "TCP/IPv6";17 else18 return af == AF_INET ? "UDP/IPv4" : "UDP/IPv6";19}20 21int 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 else35 printf("%s does not support SO_PEEK_OFF\n", afstr(af, proto));36 close(s);37 }38 return ret;39}40 41static 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 47static 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 57static 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;160out: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 170static 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 192int 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