Path: blob/main/tools/regression/netinet/tcpsocktimewait/tcpsocktimewait.c
39491 views
/*-1* Copyright (c) 2006 Robert N. M. Watson2* Copyright (c) 2011 Juniper Networks, Inc.3* All rights reserved.4*5* Portions of this software were developed by Robert N. M. Watson under6* contract to Juniper Networks, Inc.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*/2930/*31* TCP regression test that opens a loopback TCP session, then closes one end32* while shutting down the other. This triggers an unusual TCP stack case in33* which an open file descriptor / socket is associated with a closed TCP34* connection.35*/3637#include <sys/types.h>38#include <sys/socket.h>3940#include <netinet/in.h>4142#include <err.h>43#include <errno.h>44#include <signal.h>45#include <stdio.h>46#include <stdlib.h>47#include <string.h>48#include <unistd.h>4950static void51tcp_server(pid_t partner, int listen_fd)52{53int error, accept_fd;5455accept_fd = accept(listen_fd, NULL, NULL);56if (accept_fd < 0) {57error = errno;58(void)kill(partner, SIGTERM);59errno = error;60err(-1, "tcp_server: accept");61}62close(accept_fd);63close(listen_fd);64}6566static void67tcp_client(pid_t partner, u_short port, int secs)68{69struct sockaddr_in sin;70int error, sock;7172sleep(1);7374sock = socket(PF_INET, SOCK_STREAM, 0);75if (sock < 0) {76error = errno;77(void)kill(partner, SIGTERM);78errno = error;79err(-1, "socket");80}8182bzero(&sin, sizeof(sin));83sin.sin_family = AF_INET;84sin.sin_len = sizeof(sin);85sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);86sin.sin_port = port;8788if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {89error = errno;90(void)kill(partner, SIGTERM);91errno = error;92err(-1, "connect");93}9495if (shutdown(sock, SHUT_RDWR) < 0) {96error = errno;97(void)kill(partner, SIGTERM);98errno = error;99err(-1, "shutdown");100}101102sleep(secs);103close(sock);104}105106int107main(int argc, char *argv[])108{109struct sockaddr_in sin;110pid_t child_pid, parent_pid;111int listen_fd;112socklen_t len;113u_short port;114115if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)116err(-1, "signal");117118/*119* Run the whole thing twice: once, with a short sleep in the client,120* so that we close before time wait runs out, and once with a long121* sleep so that the time wait terminates while the socket is open.122* We don't reuse listen sockets between runs.123*/124listen_fd = socket(PF_INET, SOCK_STREAM, 0);125if (listen_fd < 0)126err(-1, "socket");127128/*129* We use the loopback, but let the kernel select a port for the130* server socket.131*/132bzero(&sin, sizeof(sin));133sin.sin_family = AF_INET;134sin.sin_len = sizeof(sin);135sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);136137if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)138err(-1, "bind");139140if (listen(listen_fd, -1) < 0)141err(-1, "listen");142143/*144* Query the port so that the client can use it.145*/146bzero(&sin, sizeof(sin));147sin.sin_family = AF_INET;148sin.sin_len = sizeof(sin);149len = sizeof(sin);150if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0)151err(-1, "getsockname");152port = sin.sin_port;153printf("Using port %d\n", ntohs(port));154155parent_pid = getpid();156child_pid = fork();157if (child_pid < 0)158err(-1, "fork");159if (child_pid == 0) {160child_pid = getpid();161tcp_server(child_pid, listen_fd);162exit(0);163} else164tcp_client(parent_pid, port, 1);165(void)kill(child_pid, SIGTERM);166close(listen_fd);167sleep(5);168169/*170* Start again, this time long sleep.171*/172listen_fd = socket(PF_INET, SOCK_STREAM, 0);173if (listen_fd < 0)174err(-1, "socket");175176/*177* We use the loopback, but let the kernel select a port for the178* server socket.179*/180bzero(&sin, sizeof(sin));181sin.sin_family = AF_INET;182sin.sin_len = sizeof(sin);183sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);184185if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)186err(-1, "bind");187188if (listen(listen_fd, -1) < 0)189err(-1, "listen");190191/*192* Query the port so that the client can use it.193*/194bzero(&sin, sizeof(sin));195sin.sin_family = AF_INET;196sin.sin_len = sizeof(sin);197len = sizeof(sin);198if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0)199err(-1, "getsockname");200port = sin.sin_port;201printf("Using port %d\n", ntohs(port));202203parent_pid = getpid();204child_pid = fork();205if (child_pid < 0)206err(-1, "fork");207if (child_pid == 0) {208child_pid = getpid();209tcp_server(parent_pid, listen_fd);210} else211tcp_client(child_pid, port, 800);212213return (0);214}215216217