1/* $NetBSD: t_socket_afinet.c,v 1.2 2022/11/17 08:34:39 ozaki-r Exp $ */ 2 3/*- 4 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5 * 6 * Copyright (c) 2019 Bjoern A. Zeeb 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31#ifdef __NetBSD__ 32__RCSID("$NetBSD: t_socket_afinet.c,v 1.2 2022/11/17 08:34:39 ozaki-r Exp $"); 33#define USE_RUMPKERNEL 1 34#else 35__FBSDID("$FreeBSD$"); 36#endif 37 38#include <sys/errno.h> 39#include <sys/socket.h> 40#include <netinet/in.h> 41#include <poll.h> 42 43#include <atf-c.h> 44 45#ifdef USE_RUMPKERNEL 46#include <rump/rump.h> 47#include <rump/rump_syscalls.h> 48 49#define socket rump_sys_socket 50#define bind rump_sys_bind 51#define listen rump_sys_listen 52#define connect rump_sys_connect 53#define write rump_sys_write 54#define poll rump_sys_poll 55#define close rump_sys_close 56 57#define RUMP_INIT() rump_init() 58#else 59#define RUMP_INIT() do { } while (0) 60#endif 61 62ATF_TC_WITHOUT_HEAD(socket_afinet); 63ATF_TC_BODY(socket_afinet, tc) 64{ 65 int sd; 66 67 RUMP_INIT(); 68 69 sd = socket(PF_INET, SOCK_DGRAM, 0); 70 ATF_CHECK(sd >= 0); 71 72 close(sd); 73} 74 75ATF_TC_WITHOUT_HEAD(socket_afinet_bind_zero); 76ATF_TC_BODY(socket_afinet_bind_zero, tc) 77{ 78 int sd, rc; 79 struct sockaddr_in sin; 80 81 RUMP_INIT(); 82 83#ifdef __NetBSD__ 84 atf_tc_expect_fail("NetBSD doesn't allow sin_family == 0 (sin_len == 0 too)"); 85#endif 86 87 if (atf_tc_get_config_var_as_bool_wd(tc, "ci", false)) 88 atf_tc_skip("doesn't work when mac_portacl(4) loaded (https://bugs.freebsd.org/238781)"); 89 90 sd = socket(PF_INET, SOCK_DGRAM, 0); 91 ATF_CHECK(sd >= 0); 92 93 bzero(&sin, sizeof(sin)); 94 /* 95 * For AF_INET we do not check the family in in_pcbbind_setup(9), 96 * sa_len gets set from the syscall argument in getsockaddr(9), 97 * so we bind to 0:0. 98 */ 99 rc = bind(sd, (struct sockaddr *)&sin, sizeof(sin)); 100 ATF_CHECK_EQ(0, rc); 101 102 close(sd); 103} 104 105ATF_TC_WITHOUT_HEAD(socket_afinet_bind_ok); 106ATF_TC_BODY(socket_afinet_bind_ok, tc) 107{ 108 int sd, rc; 109 struct sockaddr_in sin; 110 111 RUMP_INIT(); 112 113 sd = socket(PF_INET, SOCK_DGRAM, 0); 114 ATF_CHECK(sd >= 0); 115 116 bzero(&sin, sizeof(sin)); 117 sin.sin_family = AF_INET; 118 sin.sin_len = sizeof(sin); 119 sin.sin_port = htons(6666); 120 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 121 rc = bind(sd, (struct sockaddr *)&sin, sizeof(sin)); 122 ATF_CHECK_EQ(0, rc); 123 124 close(sd); 125} 126 127#ifdef POLLRDHUP 128ATF_TC_WITHOUT_HEAD(socket_afinet_poll_no_rdhup); 129ATF_TC_BODY(socket_afinet_poll_no_rdhup, tc) 130{ 131 int ss, ss2, cs, rc; 132 struct sockaddr_in sin; 133 struct pollfd pfd; 134 int one = 1; 135 136 RUMP_INIT(); 137 138 /* Verify that we don't expose POLLRDHUP if not requested. */ 139 140 /* Server setup. */ 141 ss = socket(PF_INET, SOCK_STREAM, 0); 142 ATF_CHECK(ss >= 0); 143 rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 144 ATF_CHECK_EQ(0, rc); 145 bzero(&sin, sizeof(sin)); 146 sin.sin_family = AF_INET; 147 sin.sin_len = sizeof(sin); 148 sin.sin_port = htons(6666); 149 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 150 rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin)); 151 ATF_CHECK_EQ(0, rc); 152 rc = listen(ss, 1); 153 ATF_CHECK_EQ(0, rc); 154 155 /* Client connects, server accepts. */ 156 cs = socket(PF_INET, SOCK_STREAM, 0); 157 ATF_CHECK(cs >= 0); 158 rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); 159 ATF_CHECK_EQ(0, rc); 160 ss2 = accept(ss, NULL, NULL); 161 ATF_CHECK(ss2 >= 0); 162 163 /* Server can write, sees only POLLOUT. */ 164 pfd.fd = ss2; 165 pfd.events = POLLIN | POLLOUT; 166 rc = poll(&pfd, 1, 0); 167 ATF_CHECK_EQ(1, rc); 168 ATF_CHECK_EQ(POLLOUT, pfd.revents); 169 170 /* Client closes socket! */ 171 rc = close(cs); 172 ATF_CHECK_EQ(0, rc); 173 174 /* 175 * Server now sees POLLIN, but not POLLRDHUP because we didn't ask. 176 * Need non-zero timeout to wait for the FIN to arrive and trigger the 177 * socket to become readable. 178 */ 179 pfd.fd = ss2; 180 pfd.events = POLLIN; 181 rc = poll(&pfd, 1, 60000); 182 ATF_CHECK_EQ(1, rc); 183 ATF_CHECK_EQ(POLLIN, pfd.revents); 184 185 close(ss2); 186 close(ss); 187} 188 189ATF_TC_WITHOUT_HEAD(socket_afinet_poll_rdhup); 190ATF_TC_BODY(socket_afinet_poll_rdhup, tc) 191{ 192 int ss, ss2, cs, rc; 193 struct sockaddr_in sin; 194 struct pollfd pfd; 195 char buffer; 196 int one = 1; 197 198 RUMP_INIT(); 199 200 /* Verify that server sees POLLRDHUP if it asks for it. */ 201 202 /* Server setup. */ 203 ss = socket(PF_INET, SOCK_STREAM, 0); 204 ATF_CHECK(ss >= 0); 205 rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); 206 ATF_CHECK_EQ(0, rc); 207 bzero(&sin, sizeof(sin)); 208 sin.sin_family = AF_INET; 209 sin.sin_len = sizeof(sin); 210 sin.sin_port = htons(6666); 211 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 212 rc = bind(ss, (struct sockaddr *)&sin, sizeof(sin)); 213 ATF_CHECK_EQ(0, rc); 214 rc = listen(ss, 1); 215 ATF_CHECK_EQ(0, rc); 216 217 /* Client connects, server accepts. */ 218 cs = socket(PF_INET, SOCK_STREAM, 0); 219 ATF_CHECK(cs >= 0); 220 rc = connect(cs, (struct sockaddr *)&sin, sizeof(sin)); 221 ATF_CHECK_EQ(0, rc); 222 ss2 = accept(ss, NULL, NULL); 223 ATF_CHECK(ss2 >= 0); 224 225 /* Server can write, so sees POLLOUT. */ 226 pfd.fd = ss2; 227 pfd.events = POLLIN | POLLOUT | POLLRDHUP; 228 rc = poll(&pfd, 1, 0); 229 ATF_CHECK_EQ(1, rc); 230 ATF_CHECK_EQ(POLLOUT, pfd.revents); 231 232 /* Client writes two bytes, server reads only one of them. */ 233 rc = write(cs, "xx", 2); 234 ATF_CHECK_EQ(2, rc); 235 rc = read(ss2, &buffer, 1); 236 ATF_CHECK_EQ(1, rc); 237 238 /* Server can read, so sees POLLIN. */ 239 pfd.fd = ss2; 240 pfd.events = POLLIN | POLLOUT | POLLRDHUP; 241 rc = poll(&pfd, 1, 0); 242 ATF_CHECK_EQ(1, rc); 243 ATF_CHECK_EQ(POLLIN | POLLOUT, pfd.revents); 244 245 /* Client closes socket! */ 246 rc = close(cs); 247 ATF_CHECK_EQ(0, rc); 248 249 /* 250 * Server sees Linux-style POLLRDHUP. Note that this is the case even 251 * though one byte of data remains unread. 252 * 253 * This races against the delivery of FIN caused by the close() above. 254 * Sometimes (more likely when run under truss or if another system 255 * call is added in between) it hits the path where sopoll_generic() 256 * immediately sees SBS_CANTRCVMORE, and sometimes it sleeps with flag 257 * SB_SEL so that it's woken up almost immediately and runs again, 258 * which is why we need a non-zero timeout here. 259 */ 260 pfd.fd = ss2; 261 pfd.events = POLLRDHUP; 262 rc = poll(&pfd, 1, 60000); 263 ATF_CHECK_EQ(1, rc); 264 ATF_CHECK_EQ(POLLRDHUP, pfd.revents); 265 266 close(ss2); 267 close(ss); 268} 269#endif /* POLLRDHUP */ 270 271ATF_TP_ADD_TCS(tp) 272{ 273 274 ATF_TP_ADD_TC(tp, socket_afinet); 275 ATF_TP_ADD_TC(tp, socket_afinet_bind_zero); 276 ATF_TP_ADD_TC(tp, socket_afinet_bind_ok); 277#ifdef POLLRDHUP 278 ATF_TP_ADD_TC(tp, socket_afinet_poll_no_rdhup); 279 ATF_TP_ADD_TC(tp, socket_afinet_poll_rdhup); 280#endif 281 282 return atf_no_error(); 283} 284