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
|
/* 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 && cqe->res != -ENETUNREACH) {
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;
}
|