Loading...
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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | /* * Copyright (c) 2021-2024 Apple Inc. All rights reserved. */ #include <stdio.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/event.h> #include <sys/time.h> #include <arpa/inet.h> #include <net/if_var.h> #include <netinet/ip6.h> #include <sys/sysctl.h> #include <darwintest.h> T_GLOBAL_META( T_META_NAMESPACE("xnu.net"), T_META_RADAR_COMPONENT_NAME("xnu"), T_META_RADAR_COMPONENT_VERSION("networking"), T_META_OWNER("rpaulo") ); /* * Tests that filling up the socket buffer doesn't cause * kevent to return "writeable". */ static void __unused test_kevent(int type) { int sockets[2] = { -1 }; int kq = -1; struct kevent evlist = { 0 }; struct kevent chlist = { 0 }; T_ASSERT_POSIX_SUCCESS((kq = kqueue()), "kqueue"); T_ASSERT_POSIX_SUCCESS(socketpair(AF_UNIX, type, 0, sockets), "socketpair"); int flags = fcntl(sockets[0], F_GETFL); T_ASSERT_POSIX_SUCCESS(fcntl(sockets[0], F_SETFL, flags | O_NONBLOCK), "fcntl"); EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, 0, 0, 0); ssize_t result = kevent(kq, &chlist, 1, &evlist, 1, NULL); T_ASSERT_EQ(result, 1, "should be able to write"); // Fill the socket buffer char buf[1] = { 0x55 }; while (write(sockets[0], buf, sizeof(buf)) > 0) { ; } result = write(sockets[0], buf, sizeof(buf)); if (type == SOCK_STREAM) { T_ASSERT_POSIX_FAILURE(result, EWOULDBLOCK, "should block"); } else { T_ASSERT_POSIX_FAILURE(result, ENOBUFS, "should block"); } struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 }; result = kevent(kq, &chlist, 1, &evlist, 1, &ts); T_ASSERT_EQ(result, 0, "should timeout"); close(sockets[0]); close(sockets[1]); close(kq); } static void __unused test_kevent_lowat(int type) { int sockets[2] = { -1 }; int kq = -1; struct kevent evlist = { 0 }; struct kevent chlist = { 0 }; T_ASSERT_POSIX_SUCCESS((kq = kqueue()), "kqueue"); T_ASSERT_POSIX_SUCCESS(socketpair(AF_UNIX, type, 0, sockets), "socketpair"); int flags = fcntl(sockets[0], F_GETFL); T_ASSERT_POSIX_SUCCESS(fcntl(sockets[0], F_SETFL, flags | O_NONBLOCK), "fcntl"); EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, 0, 0, 0); ssize_t result = kevent(kq, &chlist, 1, &evlist, 1, NULL); T_ASSERT_EQ(result, 1, "should be able to write"); // Almost fill the socket buffer but leave 2K available. char buf[1] = { 0x55 }; int max_writes = type == SOCK_STREAM ? 6000 : 30; for (int i = 0; i < max_writes; i++) { write(sockets[0], buf, sizeof(buf)); } result = kevent(kq, &chlist, 1, &evlist, 1, NULL); T_ASSERT_EQ(result, 1, "should be able to write again"); char large_buf[4096] = { }; if (type == SOCK_STREAM) { // Write 2KB. result = write(sockets[0], large_buf, 2 * 1024); T_ASSERT_POSIX_SUCCESS(result, "write 2KB"); // Write 4KB, should fail. result = write(sockets[0], large_buf, sizeof(large_buf)); T_ASSERT_POSIX_FAILURE(result, EWOULDBLOCK, "should block (EWOULDBLOCK)"); } else { // Write 512B. result = write(sockets[0], large_buf, 512); T_ASSERT_POSIX_SUCCESS(result, "write 512B"); // Write 2KB, should fail. result = write(sockets[0], large_buf, 2048); T_ASSERT_POSIX_FAILURE(result, ENOBUFS, "should block (ENOBUFS)"); } // Ask kqueue to wake us up when we can write 100 bytes. EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, NOTE_LOWAT, 100, 0); struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 }; result = kevent(kq, &chlist, 1, &evlist, 1, &ts); T_ASSERT_EQ(result, 0, "should timeout (note_lowat)"); // Set the send buffer low water mark. int lowat = type == SOCK_STREAM ? 100 : 10; result = setsockopt(sockets[0], SOL_SOCKET, SO_SNDLOWAT, &lowat, sizeof(lowat)); T_ASSERT_POSIX_SUCCESS(result, "setsockopt"); if (type == SOCK_STREAM) { // Write 100 bytes. result = write(sockets[0], large_buf, 100); T_ASSERT_POSIX_SUCCESS(result, "write 100B"); } // Reset the event and kqueue should respect SO_SNDLOWAT. EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, 0, 0, 0); result = kevent(kq, &chlist, 1, &evlist, 1, &ts); T_ASSERT_EQ(result, 0, "should timeout (sndlowat)"); close(sockets[0]); close(sockets[1]); close(kq); } T_DECL(uipc_kevent, "Tests the UNIX Domain kevent filter", T_META_CHECK_LEAKS(false), T_META_TAG_VM_PREFERRED) { #if 0 test_kevent(SOCK_STREAM); test_kevent(SOCK_DGRAM); test_kevent_lowat(SOCK_STREAM); test_kevent_lowat(SOCK_DGRAM); #else T_SKIP("Test is unstable"); #endif } |