1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /*
4 * Copyright (C) 2008, Linux Foundation,
5 * Copyright (c) 2020 Petr Vorel <petr.vorel@gmail.com>
6 * written by Michael Kerrisk <mtk.manpages@gmail.com>
7 * Initial Porting to LTP by Subrata <subrata@linux.vnet.ibm.com>
8 */
9
10 #define _GNU_SOURCE
11 #include <unistd.h>
12 #include <sys/syscall.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <stdlib.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <linux/net.h>
21
22 #include "tst_test.h"
23 #include "lapi/fcntl.h"
24 #include "lapi/syscalls.h"
25
26 #define PORT_NUM 33333
27
28 static const char *variant_desc[] = {
29 "libc accept4()",
30 "__NR_accept4 syscall",
31 "__NR_socketcall SYS_ACCEPT4 syscall"};
32
33 static struct sockaddr_in *conn_addr, *accept_addr;
34 static int listening_fd;
35
socketcall_accept4(int fd,struct sockaddr * sockaddr,socklen_t * addrlen,int flags)36 static int socketcall_accept4(int fd, struct sockaddr *sockaddr, socklen_t
37 *addrlen, int flags)
38 {
39 long args[6];
40
41 args[0] = fd;
42 args[1] = (long)sockaddr;
43 args[2] = (long)addrlen;
44 args[3] = flags;
45
46 return tst_syscall(__NR_socketcall, SYS_ACCEPT4, args);
47 }
48
create_listening_socket(void)49 static int create_listening_socket(void)
50 {
51 struct sockaddr_in svaddr;
52 int lfd;
53 int optval;
54
55 memset(&svaddr, 0, sizeof(struct sockaddr_in));
56 svaddr.sin_family = AF_INET;
57 svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
58 svaddr.sin_port = htons(PORT_NUM);
59
60 lfd = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0);
61
62 optval = 1;
63 SAFE_SETSOCKOPT(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
64 SAFE_BIND(lfd, (struct sockaddr *)&svaddr, sizeof(struct sockaddr_in));
65 SAFE_LISTEN(lfd, 5);
66
67 return lfd;
68 }
69
setup(void)70 static void setup(void)
71 {
72 tst_res(TINFO, "Testing variant: %s", variant_desc[tst_variant]);
73
74 memset(conn_addr, 0, sizeof(*conn_addr));
75 conn_addr->sin_family = AF_INET;
76 conn_addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
77 conn_addr->sin_port = htons(PORT_NUM);
78
79 listening_fd = create_listening_socket();
80 }
81
cleanup(void)82 static void cleanup(void)
83 {
84 SAFE_CLOSE(listening_fd);
85 }
86
87 static struct test_case {
88 int cloexec;
89 int nonblock;
90 } tcases[] = {
91 { 0, 0 },
92 { SOCK_CLOEXEC, 0 },
93 { 0, SOCK_NONBLOCK },
94 { SOCK_CLOEXEC, SOCK_NONBLOCK },
95 };
96
verify_accept4(unsigned int nr)97 static void verify_accept4(unsigned int nr)
98 {
99 struct test_case *tcase = &tcases[nr];
100 int flags = tcase->cloexec | tcase->nonblock;
101 int connfd, acceptfd;
102 int fdf, flf, fdf_pass, flf_pass, fd_cloexec, fd_nonblock;
103 socklen_t addrlen;
104
105 connfd = SAFE_SOCKET(AF_INET, SOCK_STREAM, 0);
106 SAFE_CONNECT(connfd, (struct sockaddr *)conn_addr, sizeof(*conn_addr));
107 addrlen = sizeof(*accept_addr);
108
109 switch (tst_variant) {
110 case 0:
111 TEST(accept4(listening_fd, (struct sockaddr *)accept_addr,
112 &addrlen, flags));
113 break;
114 case 1:
115 TEST(tst_syscall(__NR_accept4, listening_fd,
116 (struct sockaddr *)accept_addr,
117 &addrlen, flags));
118 break;
119 case 2:
120 TEST(socketcall_accept4(listening_fd, (struct sockaddr *)accept_addr,
121 &addrlen, flags));
122 break;
123 }
124
125 if (TST_RET == -1)
126 tst_brk(TBROK | TTERRNO, "accept4 failed");
127
128 acceptfd = TST_RET;
129
130 /* Test to see if O_CLOEXEC is as expected */
131 fdf = SAFE_FCNTL(acceptfd, F_GETFD);
132 fd_cloexec = !!(fdf & FD_CLOEXEC);
133 fdf_pass = fd_cloexec == !!tcase->cloexec;
134 if (!fdf_pass) {
135 tst_res(TFAIL, "Close-on-exec flag mismatch, %d vs %d",
136 fd_cloexec, !!tcase->cloexec);
137 }
138
139 /* Test to see if O_NONBLOCK is as expected */
140 flf = SAFE_FCNTL(acceptfd, F_GETFL);
141 fd_nonblock = !!(flf & O_NONBLOCK);
142 flf_pass = fd_nonblock == !!tcase->nonblock;
143 if (!flf_pass) {
144 tst_res(TFAIL, "nonblock flag mismatch, %d vs %d",
145 fd_nonblock, !!tcase->nonblock);
146 }
147
148 SAFE_CLOSE(acceptfd);
149 SAFE_CLOSE(connfd);
150
151 if (fdf_pass && flf_pass) {
152 tst_res(TPASS, "Close-on-exec %d, nonblock %d",
153 fd_cloexec, fd_nonblock);
154 }
155 }
156
157 static struct tst_test test = {
158 .tcnt = ARRAY_SIZE(tcases),
159 .setup = setup,
160 .cleanup = cleanup,
161 .test_variants = 3,
162 .test = verify_accept4,
163 .bufs = (struct tst_buffers []) {
164 {&conn_addr, .size = sizeof(*conn_addr)},
165 {&accept_addr, .size = sizeof(*accept_addr)},
166 {},
167 }
168 };
169