1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
#include "../config-host.h"
/* SPDX-License-Identifier: MIT */
/*
* Check that IORING_OP_CONNECT properly returns -ECONNRESET when
* attempting to connect to an unreachable address. See:
*
* https://github.com/axboe/liburing/discussions/1335
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "liburing.h"
#include "helpers.h"
static int check_cqe(struct io_uring *ring, struct io_uring_cqe *cqe)
{
if (cqe->res == -EINVAL)
return T_EXIT_SKIP;
switch (cqe->user_data) {
case 1:
if (cqe->res != -ECONNRESET) {
fprintf(stderr, "Unexpected connect: %d\n", cqe->res);
return T_EXIT_FAIL;
}
break;
case 2:
if (cqe->res) {
fprintf(stderr, "Unexpected shutdown: %d\n", cqe->res);
return T_EXIT_FAIL;
}
break;
}
io_uring_cqe_seen(ring, cqe);
return T_EXIT_PASS;
}
static int test(struct io_uring *ring, struct sockaddr_in *saddr, int p_fd)
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe1, *sqe2;
socklen_t val_len;
int ret, val;
sqe1 = io_uring_get_sqe(ring);
io_uring_prep_connect(sqe1, p_fd, (struct sockaddr *)saddr, sizeof(*saddr));
sqe1->user_data = 1;
ret = io_uring_submit(ring);
assert(ret != -1);
usleep(200000); // 200ms
sqe2 = io_uring_get_sqe(ring);
io_uring_prep_shutdown(sqe2, p_fd, SHUT_RDWR);
sqe2->user_data = 2;
ret = io_uring_submit(ring);
assert(ret != -1);
ret = io_uring_wait_cqe(ring, &cqe);
if (ret < 0) {
fprintf(stderr, "wait: %s\n", strerror(-ret));
return T_EXIT_FAIL;
}
ret = check_cqe(ring, cqe);
if (ret != T_EXIT_PASS)
return ret;
val = 0;
val_len = sizeof(val);
ret = getsockopt(p_fd, SOL_SOCKET, SO_ERROR, &val, &val_len);
assert(ret != -1);
ret = io_uring_wait_cqe(ring, &cqe);
if (ret < 0) {
fprintf(stderr, "wait: %s\n", strerror(-ret));
return T_EXIT_FAIL;
}
return check_cqe(ring, cqe);
}
int main(int argc, char *argv[])
{
struct sockaddr_in addr = { };
struct io_uring ring;
int val, p_fd, ret;
if (argc > 1)
return T_EXIT_SKIP;
p_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
val = 1;
ret = setsockopt(p_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
assert(ret != -1);
// NB. these are to make things faster
val = 2;
ret = setsockopt(p_fd, IPPROTO_TCP, TCP_SYNCNT, &val, sizeof(val));
assert(ret != -1);
val = 500; // 500ms
ret = setsockopt(p_fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &val, sizeof(val));
assert(ret != -1);
t_set_nonblock(p_fd);
addr.sin_family = AF_INET;
/* any unreachable address */
addr.sin_addr.s_addr = inet_addr("172.31.5.5");
addr.sin_port = htons(12345);
ret = io_uring_queue_init(2, &ring, 0);
assert(ret >= 0);
ret = test(&ring, &addr, p_fd);
io_uring_queue_exit(&ring);
return ret;
}
|