1 /*
2 * Copyright (c) 2021 Apple Inc. All rights reserved.
3 */
4 #include <stdio.h>
5 #include <errno.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11
12 #include <sys/types.h>
13 #include <sys/event.h>
14 #include <sys/time.h>
15
16 #include <arpa/inet.h>
17 #include <net/if_var.h>
18 #include <netinet/ip6.h>
19 #include <sys/sysctl.h>
20 #include <darwintest.h>
21
22 /*
23 * Tests that filling up the socket buffer doesn't cause
24 * kevent to return "writeable".
25 */
26 static void __unused
test_kevent(int type)27 test_kevent(int type)
28 {
29 int sockets[2] = { -1 };
30 int kq = -1;
31 struct kevent evlist = { 0 };
32 struct kevent chlist = { 0 };
33
34 T_ASSERT_POSIX_SUCCESS((kq = kqueue()), "kqueue");
35 T_ASSERT_POSIX_SUCCESS(socketpair(AF_UNIX, type, 0, sockets), "socketpair");
36 int flags = fcntl(sockets[0], F_GETFL);
37 T_ASSERT_POSIX_SUCCESS(fcntl(sockets[0], F_SETFL, flags | O_NONBLOCK), "fcntl");
38
39 EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, 0, 0, 0);
40 ssize_t result = kevent(kq, &chlist, 1, &evlist, 1, NULL);
41 T_ASSERT_EQ(result, 1, "should be able to write");
42
43 // Fill the socket buffer
44 char buf[1] = { 0x55 };
45 while (write(sockets[0], buf, sizeof(buf)) > 0) {
46 ;
47 }
48
49 result = write(sockets[0], buf, sizeof(buf));
50 if (type == SOCK_STREAM) {
51 T_ASSERT_POSIX_FAILURE(result, EWOULDBLOCK, "should block");
52 } else {
53 T_ASSERT_POSIX_FAILURE(result, ENOBUFS, "should block");
54 }
55
56 struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
57 result = kevent(kq, &chlist, 1, &evlist, 1, &ts);
58 T_ASSERT_EQ(result, 0, "should timeout");
59 close(sockets[0]);
60 close(sockets[1]);
61 close(kq);
62 }
63
64 static void __unused
test_kevent_lowat(int type)65 test_kevent_lowat(int type)
66 {
67 int sockets[2] = { -1 };
68 int kq = -1;
69 struct kevent evlist = { 0 };
70 struct kevent chlist = { 0 };
71
72 T_ASSERT_POSIX_SUCCESS((kq = kqueue()), "kqueue");
73 T_ASSERT_POSIX_SUCCESS(socketpair(AF_UNIX, type, 0, sockets), "socketpair");
74 int flags = fcntl(sockets[0], F_GETFL);
75 T_ASSERT_POSIX_SUCCESS(fcntl(sockets[0], F_SETFL, flags | O_NONBLOCK), "fcntl");
76
77 EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, 0, 0, 0);
78 ssize_t result = kevent(kq, &chlist, 1, &evlist, 1, NULL);
79 T_ASSERT_EQ(result, 1, "should be able to write");
80
81 // Almost fill the socket buffer but leave 2K available.
82 char buf[1] = { 0x55 };
83 int max_writes = type == SOCK_STREAM ? 6000 : 30;
84 for (int i = 0; i < max_writes; i++) {
85 write(sockets[0], buf, sizeof(buf));
86 }
87
88 result = kevent(kq, &chlist, 1, &evlist, 1, NULL);
89 T_ASSERT_EQ(result, 1, "should be able to write again");
90
91 char large_buf[4096] = { };
92
93 if (type == SOCK_STREAM) {
94 // Write 2KB.
95 result = write(sockets[0], large_buf, 2 * 1024);
96 T_ASSERT_POSIX_SUCCESS(result, "write 2KB");
97 // Write 4KB, should fail.
98 result = write(sockets[0], large_buf, sizeof(large_buf));
99 T_ASSERT_POSIX_FAILURE(result, EWOULDBLOCK, "should block (EWOULDBLOCK)");
100 } else {
101 // Write 512B.
102 result = write(sockets[0], large_buf, 512);
103 T_ASSERT_POSIX_SUCCESS(result, "write 512B");
104 // Write 2KB, should fail.
105 result = write(sockets[0], large_buf, 2048);
106 T_ASSERT_POSIX_FAILURE(result, ENOBUFS, "should block (ENOBUFS)");
107 }
108
109 // Ask kqueue to wake us up when we can write 100 bytes.
110 EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, NOTE_LOWAT, 100, 0);
111
112 struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
113 result = kevent(kq, &chlist, 1, &evlist, 1, &ts);
114 T_ASSERT_EQ(result, 0, "should timeout (note_lowat)");
115
116 // Set the send buffer low water mark.
117 int lowat = type == SOCK_STREAM ? 100 : 10;
118 result = setsockopt(sockets[0], SOL_SOCKET, SO_SNDLOWAT, &lowat, sizeof(lowat));
119 T_ASSERT_POSIX_SUCCESS(result, "setsockopt");
120
121 if (type == SOCK_STREAM) {
122 // Write 100 bytes.
123 result = write(sockets[0], large_buf, 100);
124 T_ASSERT_POSIX_SUCCESS(result, "write 100B");
125 }
126
127 // Reset the event and kqueue should respect SO_SNDLOWAT.
128 EV_SET(&chlist, sockets[0], EVFILT_WRITE, EV_ADD | EV_ERROR, 0, 0, 0);
129 result = kevent(kq, &chlist, 1, &evlist, 1, &ts);
130 T_ASSERT_EQ(result, 0, "should timeout (sndlowat)");
131
132 close(sockets[0]);
133 close(sockets[1]);
134 close(kq);
135 }
136
137 T_DECL(uipc_kevent, "Tests the UNIX Domain kevent filter", T_META_CHECK_LEAKS(false), T_META_TAG_VM_PREFERRED)
138 {
139 #if 0
140 test_kevent(SOCK_STREAM);
141 test_kevent(SOCK_DGRAM);
142 test_kevent_lowat(SOCK_STREAM);
143 test_kevent_lowat(SOCK_DGRAM);
144 #else
145 T_SKIP("Test is unstable");
146 #endif
147 }
148