197403Sobrien/*- 2169691Skan * Copyright (c) 2005 Robert N. M. Watson 3169691Skan * All rights reserved. 497403Sobrien * 5132720Skan * Redistribution and use in source and binary forms, with or without 697403Sobrien * modification, are permitted provided that the following conditions 7132720Skan * are met: 897403Sobrien * 1. Redistributions of source code must retain the above copyright 997403Sobrien * notice, this list of conditions and the following disclaimer. 1097403Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1197403Sobrien * notice, this list of conditions and the following disclaimer in the 12132720Skan * documentation and/or other materials provided with the distribution. 1397403Sobrien * 1497403Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1597403Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1697403Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1797403Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18132720Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19169691Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20169691Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2197403Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2297403Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2397403Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2497403Sobrien * SUCH DAMAGE. 2597403Sobrien * 2697403Sobrien * $FreeBSD$ 2797403Sobrien */ 2897403Sobrien 2997403Sobrien#include <sys/types.h> 3097403Sobrien#include <sys/socket.h> 3197403Sobrien#include <sys/sysctl.h> 3297403Sobrien 3397403Sobrien#include <err.h> 34169691Skan#include <errno.h> 3597403Sobrien#include <stdio.h> 36169691Skan#include <stdlib.h> 3797403Sobrien#include <unistd.h> 38169691Skan 3997403Sobrien/* 40102782Skan * This regression test is intended to validate that the backlog parameter 4197403Sobrien * set by listen() is properly set, can be retrieved using SO_LISTENQLIMIT, 42169691Skan * and that it can be updated by later calls to listen(). We also check that 4397403Sobrien * SO_LISTENQLIMIT cannot be set. 44169691Skan * 45169691Skan * Future things to test: 46169691Skan * 47169691Skan * - That if we change the value of kern.ipc.somaxconn, the limits really 48169691Skan * do change. 49169691Skan * 50169691Skan * - That limits are, approximately, enforced and implemented. 51169691Skan * 52169691Skan * - All this on multiple socket types -- i.e., PF_LOCAL. 53169691Skan * 54169691Skan * - That we also test SO_LISTENQLEN and SO_LISTENINCQLEN. 55169691Skan */ 5697403Sobrien 5797403Sobrien/* 5897403Sobrien * We retrieve kern.ipc.somaxconn before running the tests in order to use a 5997403Sobrien * run-time set value of SOMAXCONN, rather than compile-time set. We assume 6097403Sobrien * that no other process will be simultaneously frobbing it, and these tests 6197403Sobrien * may fail if that assumption is not held. 6297403Sobrien */ 6397403Sobrienstatic int somaxconn; 6497403Sobrien 6597403Sobrien/* 6697403Sobrien * Retrieve the current socket listen queue limit using SO_LISTENQLIMIT. 6797403Sobrien */ 6897403Sobrienstatic int 6997403Sobriensocket_get_backlog(int sock, int *backlogp, const char *testclass, 7097403Sobrien const char *test, const char *testfunc) 7197403Sobrien{ 7297403Sobrien socklen_t len; 7397403Sobrien int i; 7497403Sobrien 7597403Sobrien len = sizeof(i); 7697403Sobrien if (getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, &len) < 0) { 7797403Sobrien warn("%s: %s: %s: socket_get_backlog: getsockopt(" 7897403Sobrien "SOL_SOCKET, SO_LISTENQLIMIT)", testclass, test, 7997403Sobrien testfunc); 8097403Sobrien return (-1); 81259694Spfg } 82259694Spfg 83259694Spfg if (len != sizeof(i)) { 84259694Spfg warnx("%s: %s: %s: socket_get_backlog: getsockopt(" 85259694Spfg "SOL_SOCKET, SO_LISTENQLIMIT): returned size %d", 86259694Spfg testclass, test, testfunc, len); 87259694Spfg return (-1); 88259694Spfg } 8997403Sobrien 9097403Sobrien *backlogp = i; 9197403Sobrien 9297403Sobrien return (0); 9397403Sobrien} 9497403Sobrien 9597403Sobrien/* 9697403Sobrien * Create a socket, check the queue limit on creation, perform a listen(), 9797403Sobrien * and make sure that the limit was set as expected by listen(). 9897403Sobrien */ 9997403Sobrienstatic int 100169691Skansocket_listen(int domain, int type, int protocol, int backlog, 10197403Sobrien int create_backlog_assertion, int listen_backlog_assertion, int *sockp, 102169691Skan const char *domainstring, const char *typestring, const char *testclass, 103169691Skan const char *test) 10497403Sobrien{ 10597403Sobrien int backlog_retrieved, sock; 10697403Sobrien 107169691Skan sock = socket(domain, type, protocol); 10897403Sobrien if (sock < 0) { 10997403Sobrien warn("%s: %s: socket_listen: socket(%s, %s)", testclass, 11097403Sobrien test, domainstring, typestring); 11197403Sobrien close(sock); 112169691Skan return (-1); 11397403Sobrien } 11497403Sobrien 11597403Sobrien if (socket_get_backlog(sock, &backlog_retrieved, testclass, test, 116169691Skan "socket_listen") < 0) { 11797403Sobrien close(sock); 11897403Sobrien return (-1); 11997403Sobrien } 12097403Sobrien 12197403Sobrien if (backlog_retrieved != create_backlog_assertion) { 12297403Sobrien warnx("%s: %s: socket_listen: create backlog is %d not %d", 12397403Sobrien testclass, test, backlog_retrieved, 12497403Sobrien create_backlog_assertion); 12597403Sobrien close(sock); 12697403Sobrien return (-1); 12797403Sobrien } 12897403Sobrien 12997403Sobrien if (listen(sock, backlog) < 0) { 13097403Sobrien warn("%s: %s: socket_listen: listen(, %d)", testclass, test, 13197403Sobrien backlog); 13297403Sobrien close(sock); 13397403Sobrien return (-1); 134169691Skan } 13597403Sobrien 13697403Sobrien if (socket_get_backlog(sock, &backlog_retrieved, testclass, test, 13797403Sobrien "socket_listen") < 0) { 13897403Sobrien close(sock); 139169691Skan return (-1); 140169691Skan } 141169691Skan 142169691Skan if (backlog_retrieved != listen_backlog_assertion) { 143169691Skan warnx("%s: %s: socket_listen: listen backlog is %d not %d", 14497403Sobrien testclass, test, backlog_retrieved, 145169691Skan listen_backlog_assertion); 146169691Skan close(sock); 14797403Sobrien return (-1); 14897403Sobrien } 14997403Sobrien 15097403Sobrien *sockp = sock; 15197403Sobrien return (0); 152169691Skan} 15397403Sobrien 15497403Sobrien/* 15597403Sobrien * This test creates sockets and tests default states before and after 15697403Sobrien * listen(). Specifically, we expect a queue limit of 0 before listen, and 15797403Sobrien * then various settings for after listen(). If the passed backlog was 158169691Skan * either < 0 or > somaxconn, it should be set to somaxconn; otherwise, the 15997403Sobrien * passed queue depth. 16097403Sobrien */ 161169691Skanstatic void 16297403Sobrientest_defaults(void) 16397403Sobrien{ 16497403Sobrien int sock; 165169691Skan 16697403Sobrien /* 167 * First pass. Confirm the default is 0. Listen with a backlog of 168 * 0 and confirm it gets set that way. 169 */ 170 if (socket_listen(PF_INET, SOCK_STREAM, 0, 0, 0, 0, &sock, "PF_INET", 171 "SOCK_STREAM", "test_defaults", "default_0_listen_0") < 0) 172 exit(-1); 173 close(sock); 174 175 /* 176 * Second pass. Listen with a backlog of -1 and make sure it is set 177 * to somaxconn. 178 */ 179 if (socket_listen(PF_INET, SOCK_STREAM, 0, -1, 0, somaxconn, &sock, 180 "PF_INET", "SOCK_STREAM", "test_defaults", "default_0_listen_-1") 181 < 0) 182 exit(-1); 183 close(sock); 184 185 /* 186 * Third pass. Listen with a backlog of 1 and make sure it is set to 187 * 1. 188 */ 189 if (socket_listen(PF_INET, SOCK_STREAM, 0, 1, 0, 1, &sock, "PF_INET", 190 "SOCK_STREAM", "test_defaults", "default_0_listen_1") < 0) 191 exit(-1); 192 close(sock); 193 194 /* 195 * Fourth pass. Listen with a backlog of somaxconn and make sure it 196 * is set to somaxconn. 197 */ 198 if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn, 0, somaxconn, 199 &sock, "PF_INET", "SOCK_STREAM", "test_defaults", 200 "default_0_listen_somaxconn") < 0) 201 exit(-1); 202 close(sock); 203 204 /* 205 * Fifth pass. Listen with a backlog of somaxconn+1 and make sure it 206 * is set to somaxconn. 207 */ 208 if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn+1, 0, somaxconn, 209 &sock, "PF_INET", "SOCK_STREAM", "test_defaults", 210 "default_0_listen_somaxconn+1") < 0) 211 exit(-1); 212 close(sock); 213} 214 215/* 216 * Create a socket, set the initial listen() state, then update the queue 217 * depth using listen(). Check that the backlog is as expected after both 218 * the first and second listen(). 219 */ 220static int 221socket_listen_update(int domain __unused, int type __unused, 222 int protocol __unused, int backlog, 223 int update_backlog, int listen_backlog_assertion, 224 int update_backlog_assertion, int *sockp, const char *domainstring, 225 const char *typestring, const char *testclass, const char *test) 226{ 227 int backlog_retrieved, sock; 228 229 sock = socket(PF_INET, SOCK_STREAM, 0); 230 if (sock < 0) { 231 warn("%s: %s: socket_listen_update: socket(%s, %s)", 232 testclass, test, domainstring, typestring); 233 return (-1); 234 } 235 236 if (listen(sock, backlog) < 0) { 237 warn("%s: %s: socket_listen_update: initial listen(, %d)", 238 testclass, test, backlog); 239 close(sock); 240 return (-1); 241 } 242 243 if (socket_get_backlog(sock, &backlog_retrieved, testclass, test, 244 "socket_listen_update") < 0) { 245 close(sock); 246 return (-1); 247 } 248 249 if (backlog_retrieved != listen_backlog_assertion) { 250 warnx("%s: %s: socket_listen_update: initial backlog is %d " 251 "not %d", testclass, test, backlog_retrieved, 252 listen_backlog_assertion); 253 close(sock); 254 return (-1); 255 } 256 257 if (listen(sock, update_backlog) < 0) { 258 warn("%s: %s: socket_listen_update: update listen(, %d)", 259 testclass, test, update_backlog); 260 close(sock); 261 return (-1); 262 } 263 264 if (socket_get_backlog(sock, &backlog_retrieved, testclass, test, 265 "socket_listen_update") < 0) { 266 close(sock); 267 return (-1); 268 } 269 270 if (backlog_retrieved != update_backlog_assertion) { 271 warnx("%s: %s: socket_listen_update: updated backlog is %d " 272 "not %d", testclass, test, backlog_retrieved, 273 update_backlog_assertion); 274 close(sock); 275 return (-1); 276 } 277 278 *sockp = sock; 279 return (0); 280} 281 282/* 283 * This test tests using listen() to update the queue depth after a socket 284 * has already been marked as listening. We test several cases: setting the 285 * socket < 0, 0, 1, somaxconn, and somaxconn + 1. 286 */ 287static void 288test_listen_update(void) 289{ 290 int sock; 291 292 /* 293 * Set to 5, update to -1, which should give somaxconn. 294 */ 295 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, -1, 5, somaxconn, 296 &sock, "PF_INET", "SOCK_STREAM", "test_listen_update", 297 "update_5,-1") < 0) 298 exit(-1); 299 close(sock); 300 301 /* 302 * Set to 5, update to 0, which should give 0. 303 */ 304 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 0, 5, 0, &sock, 305 "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,0") 306 < 0) 307 exit(-1); 308 close(sock); 309 310 /* 311 * Set to 5, update to 1, which should give 1. 312 */ 313 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 1, 5, 1, &sock, 314 "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,1") 315 < 0) 316 exit(-1); 317 close(sock); 318 319 /* 320 * Set to 5, update to somaxconn, which should give somaxconn. 321 */ 322 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn, 5, 323 somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update", 324 "update_5,somaxconn") < 0) 325 exit(-1); 326 close(sock); 327 328 /* 329 * Set to 5, update to somaxconn+1, which should give somaxconn. 330 */ 331 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn+1, 5, 332 somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update", 333 "update_5,somaxconn+1") < 0) 334 exit(-1); 335 close(sock); 336} 337 338/* 339 * SO_LISTENQLIMIT is a read-only socket option, so make sure we get an error 340 * if we try to write it. 341 */ 342static void 343test_set_qlimit(void) 344{ 345 int i, ret, sock; 346 347 sock = socket(PF_INET, SOCK_STREAM, 0); 348 if (sock < 0) 349 err(-1, "test_set_qlimit: socket(PF_INET, SOCK_STREAM)"); 350 351 i = 0; 352 ret = setsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, sizeof(i)); 353 if (ret < 0 && errno != ENOPROTOOPT) { 354 warn("test_set_qlimit: setsockopt(SOL_SOCKET, " 355 "SO_LISTENQLIMIT, 0): unexpected error"); 356 close(sock); 357 } 358 359 if (ret == 0) { 360 warnx("test_set_qlimit: setsockopt(SOL_SOCKET, " 361 "SO_LISTENQLIMIT, 0) succeeded"); 362 close(sock); 363 exit(-1); 364 } 365 close(sock); 366} 367 368int 369main(void) 370{ 371 size_t len; 372 373 len = sizeof(somaxconn); 374 if (sysctlbyname("kern.ipc.somaxconn", &somaxconn, &len, NULL, 0) 375 < 0) 376 err(-1, "sysctlbyname(kern.ipc.somaxconn)"); 377 378 test_defaults(); 379 test_listen_update(); 380 test_set_qlimit(); 381 382 return (0); 383} 384