[OpenVMS, TCP/IP] FIN_WAIT_2 state sockets as per netstat -an discussion.
PRODUCT: TCP/IP Version 5.6 ECO 4
OP/SYS: OpenVMS IA64 Version 8.3-1H1
COMPONENTS: sysconfig TCPIP$ETC:SYSCONFIGTAB.DAT
SOURCE: Philippe Vouters Fontainebleau/France
HIGH QUALITY MOBILES+TABLETS: http://android-land.fr
OVERVIEW: FIN_WAIT_2 state sockets are caused by the remote peer not programming a socket close, causing a TCP/IP protocol FIN ACK never to be received. FIN_WAIT_2 state socket as per netstat -an use two system administrator settable timer timeout values. On HP-UX as well as on Linux, an explicit fin_wait_2 timeout may be configured. On HP-UX, you would use the ndd utility. On Linux, this is done done using sysctl. With TCP/IP for OpenVMS systems, the FIN_WAIT_2 timer timeout is computed using the following formula : tcp_keepcnt * tcp_keepintvl / 2. On OpenVMS, both tcp_keepcnt and tcp_keepintvl are system admininistrator settable, either dynamically using $ sysconfig -r inet <parameter>=value or taken into account after reboot by writing or adding the following information in TCPIP$ETC:SYSCONFIGTAB.DAT: inet: tcp_keepcnt=value tcp_keepintvl=value To know what are each sysconfig parameter value type and accepted value range, use the sysconfig "-Q" option. For example: $ sysconfig -"Q" inet tcp_keepcnt tcp_keepintvl inet: tcp_keepcnt - type=INT op=CRQ min_val=1 max_val=32767 tcp_keepintvl - type=INT op=CRQ min_val=2 max_val=32767
*** CAUTION *** This sample procedure has been tested using TCP/IP V5.6 ECO 4 on OpenVMS IA64 V8.3-1H1. However, we cannot guarantee its effectiveness because of the possibility of error in transmitting or implementing it. It is meant to be used as a template for writing your own procedure, and may require modification for use on your system.
PROCEDURE NOTES: The aim of this DCL procedure is to check that no FIN_WAIT_2 state socket remains visible after an elapsed time on localhost port 50000. Only a CLOSE_WAIT state socket ought to be observed denoting the voluntary lack of shutdown+close C API calls on the server side of the peer-to-peer connection. Depending on the speed the two SPAWN/NOWAIT subprocesses are created and effectively running, this DCL procedure accepts an added wait time parameter as P1 expressed in seconds unit. If you do not set this P1 parameter, the default added wait time is 30 seconds. Provided you wish to observe a FIN_WAIT_2 state socket, you may set P1 as "-1". Assuming your process is granted enough privileges, when using the following TCP/IP commands: $ @sys$manager:tcpip$define_commands $! Set fin_wait_2 timer timeout to 60 seconds $ sysconfig -r inet tcp_keepintvl=60 tcp_keepcnt=2 The DCL procedure below displays the following information: $ @FIN_WAIT_2 %DCL-S-SPAWNED, process __SERVER spawned %DCL-S-SPAWNED, process __CLIENT spawned Awaiting for tcp_keepintvl*tcp_keepcnt/2 = 60 + 30 seconds Gethostbyname for remote nodename. Bind name to ls. Listen on ls for connections. Selecting ... Open socket 1: AF_INET, SOCK_STREAM. Connect socket 1 to sock2_name. after : emask=0x0 rmask=0x8 ret=1 Accept connection from ls Selecting ... after : emask=0x0 rmask=0x10 ret=1 recv()==0 => peer disconnected... Selecting ... tcp 0 0 127.0.0.1.50000 127.0.0.1.54525 CLOSE_WAIT %SYSTEM-F-FORCEX, forced exit of image from DCL STOP/IMAGE by another process %SYSTEM-F-FORCEX, forced exit of image from DCL STOP/IMAGE by another process
PROCEDURE: $! $! FILE NAME : FIN_WAIT_2.COM $! $! $! COPYRIGHT (C) 2009 BY $! HEWLETT-PACKARD COMPANY $! ALL RIGHTS RESERVED. $! $! THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED $! ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE INCLUSION $! OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER COPIES $! THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER $! PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY TRANSFERRED. $! $! THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND $! SHOULD NOT BE CONSTRUED AS A COMMITMENT BY HEWLETT-PACKARD COMPANY. $! $! HP ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS $! SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY HP. $! $! NO RESPONSIBILITY IS ASSUMED FOR THE USE OR RELIABILITY OF SOFTWARE $! ON EQUIPMENT THAT IS NOT SUPPLIED BY HEWLETT-PACKARD COMPANY. $! $! SUPPORT FOR THIS SOFTWARE IS NOT COVERED UNDER ANY HP SOFTWARE $! PRODUCT SUPPORT CONTRACT, BUT MAY BE PROVIDED UNDER THE TERMS OF THE $! CONSULTING AGREEMENT UNDER WHICH THIS SOFTWARE WAS DEVELOPED. $! $ added_fin_wait_2_time = 30 $ if "''P1'" .nes. "" then added_fin_wait_2_time = f$integer("''P1'") $ create server.c #include <errno.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <signal.h> #ifdef sun #include <sys/sockio.h> #endif #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <sys/uio.h> #include <sys/ioctl.h> #include <unistd.h> /* * Functional Description * * This example creates a socket of type SOCK_STREAM (TCP), * binds and listens on the socket, receives a message * and closes the connection. * Error messages are printed to the screen. * * IPC calls used: * accept * bind * close * gethostbyname * listen * recv * shutdown * socket * * * Formal Parameters * The server program expects one parameter: * portnumber ... port number where it will listen * * * Routine Value * * Status */ #ifndef FD_SET #ifndef __DECC struct fd_set_struct {u_char fds_bits[64/8];}; typedef struct fd_set_struct fd_set; #endif #define NFDBITS sizeof(fd_set)/sizeof (u_char) #define FD_SETSIZE NFDBITS #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) #define FD_ZERO(p) memset((char *)(p), 0,sizeof(*(p))) #endif int ls; cleanup(int shut,int s) { int retval; /* * Shutdown and close sock1 completely. */ if (shut){ retval = shutdown(s,2); if (retval == -1) perror ("shutdown"); } retval = close (s); if (retval) perror ("close"); } /* end cleanup*/ void sighandler(int sig){ if (sig == SIGINT){ cleanup(0, ls); exit(EXIT_SUCCESS); } } void declsighandler(){ struct sigaction action; sigemptyset(&action.sa_mask); sigaddset(&action.sa_mask,SIGINT); action.sa_flags = 0; action.sa_handler = sighandler; sigaction(SIGINT,&action,NULL); } /*--------------------------------------------------------------------*/ main(int argc, char **argv) { int s; /* sockets */ char message[BUFSIZ]; struct sockaddr_in s_name; /* Address struct for s.*/ struct hostent *hp; /* Pointer to hostent data. */ char hostname[256]; /* Name of local host. */ int flag; int opt=1; int retval; /* helpful for debugging */ int status; #ifdef __VMS size_t namelength; #else int namelength; #endif int atmark; fd_set imask,rmask,wmask,emask; int ret; /* * Check input parameters. */ if (argc != 2 ){ printf("Usage: server portnumber.\n"); exit(EXIT_FAILURE); } /* * Open socket 2: AF_INET, SOCK_STREAM. */ if ((ls = socket (AF_INET, SOCK_STREAM, 0)) == -1){ perror( "socket"); exit(EXIT_FAILURE); } if (setsockopt(ls,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof opt)<0){ printf ("setsockopt (SO_RESUSEADDR): %s\r\n",strerror(errno)); exit(EXIT_FAILURE); } /* * Get the host local name. */ retval = gethostname(hostname,sizeof hostname); if (retval) { perror ("gethostname"); cleanup (0,ls); exit(EXIT_FAILURE); } /* * Get pointer to network data structure for socket 2. */ if ((hp = gethostbyname (hostname)) == NULL) { perror( "gethostbyname"); cleanup(0, ls); exit(EXIT_FAILURE); } /* * Fill in the name & address structure for socket 2. */ s_name.sin_family = hp->h_addrtype; s_name.sin_port = htons(atoi(argv[1])); s_name.sin_addr.s_addr = htonl(INADDR_ANY); /* * Bind name to socket 2. */ printf(" \t Bind name to ls. \n"); retval = bind (ls,(struct sockaddr *)&s_name, sizeof s_name); if (retval) { perror("bind"); cleanup(0, ls); exit(EXIT_FAILURE); } /* * Listen on socket 2 for connections. */ printf(" \t Listen on ls for connections. \n"); retval = listen (ls, 5); if (retval) { perror("listen"); cleanup(0, ls); exit(EXIT_FAILURE); } declsighandler(); FD_ZERO(&imask); FD_SET(ls,&imask); for (;;){ loop:; memcpy(&rmask,&imask,sizeof imask); memcpy(&wmask,&imask,sizeof imask); memcpy(&emask,&imask,sizeof imask); printf(" Selecting ...\n"); ret=select(getdtablesize(),&rmask, NULL,&emask,NULL); printf(" after : emask=0x%x rmask=0x%x ret=%d\n\n", *((unsigned long *)&emask), *((unsigned long *)&rmask), ret); if (ret == -1){ perror ("Select"); exit(EXIT_FAILURE); } for (s=0;s<getdtablesize();s++){ if (FD_ISSET(s,&rmask)){ if (s == ls){ /* * Accept connection from socket 2: * accepted connection will be on socket 3. */ printf(" \t Accept connection from ls \n"); namelength = sizeof (s_name); s = accept (ls, (struct sockaddr *)&s_name, &namelength); if (s == -1) { perror ("accept"); cleanup(0, ls); exit(EXIT_FAILURE); } FD_SET(s,&imask); continue; } if (recv(s, message ,1,0)==0){ printf ("\t recv()==0 => peer disconnected...\n"); #ifndef FIN_WAIT_2_HANG /* * Forgetting to close the socket on only one side causes * the sockets to stay in CLOSE_WAIT/FIN_WAIT_2 as long as * the program is not exited or aborted. */ cleanup(1,s); #endif FD_CLR(s,&imask); continue; } else printf ("NORMAL message: %s\n",message); } if FD_ISSET(s,&emask){ retval = recv(s, message ,sizeof (message), MSG_OOB); printf(" \t -> retval = %d.\n",retval); /* ped */ if (retval == -1) { if (errno == EINVAL) { ioctl(s,SIOCATMARK,&atmark); printf("\t \t atmark = %d\n",atmark); printf ("\t \t PEEK ret = %d\n",recv(s, message ,1,MSG_PEEK)); printf (" \t PEEK message: %s\n", message); continue; } else { perror ("receive"); cleanup( 1, s); exit(EXIT_FAILURE); } } else printf (" \t OOB message: %s\n", message); } } } /* * Call cleanup to shutdown and close sockets. */ cleanup(0, ls); exit(EXIT_SUCCESS); } /* end main */ $ cc/define=(FD_SETSIZE=32768,FIN_WAIT_2_HANG) server $ link server $ create client.c #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #ifdef __VMS #include <unixio.h> #else #include <sys/uio.h> #endif /* * * MACRO DEFINITIONS * */ #ifndef vms #define TRUE 1 #define FALSE 0 #endif /* * Functional Description * * This example creates a socket of type SOCK_STREAM (TCP), * initiates a connection to the remote host, sends * a message to the remote host, and closes the connection. * Error messages are printed to the screen. * * IPC calls used: * close * connect * gethostbyname * send * shutdown * socket * * * Formal Parameters * The client program expects two parameters: * hostname ... name of remote host * portnumber ... port where remote host(server) is listening * * * Routine Value * * Status */ /*-----------------------------------------------------------*/ void cleanup(int shut, int s) { int retval; /* * Shutdown socket completely -- only if it was connected */ if (shut) { retval = shutdown(s,shut); if (retval == -1) perror ("shutdown"); } /* * Close socket. */ retval = close (s); if (retval) perror ("close"); } /* end cleanup */ /*--------------------------------------------------------------------*/ main(int argc,char **argv){ int sock_1; /* socket */ static char message[] = "Hi there."; struct sockaddr_in sock2_name; /* Address struct for socket2.*/ struct hostent hostentstruct; /* Storage for hostent data. */ struct hostent *hp; /* Pointer to hostent data. */ static char hostname[256]; /* Name of local host. */ int flag; int retval; /* helpful for debugging */ int shut = 0; /* flag to cleanup */ int status; /* For return status - ped - */ struct linger linger; /* * Check input parameters. */ if (argc != 3 ) { printf("Usage: client hostname portnumber.\n"); exit(1); } /* *Get pointer to network data structure for socket 2 (remote host). */ printf(" \t Gethostbyname for remote nodename. \n"); if ((hp = gethostbyname (argv[1])) == NULL){ perror( "gethostbyname"); exit(1); } /* * Copy hostent data to safe storage. */ hostentstruct = *hp; /* * Fill in the name & address structure for socket 2. */ sock2_name.sin_family = hostentstruct.h_addrtype; sock2_name.sin_port = htons(atoi(argv[2])); sock2_name.sin_addr = * ((struct in_addr *) hostentstruct.h_addr); while(1){ /* * Open socket 1: AF_INET, SOCK_STREAM. */ printf(" \t Open socket 1: AF_INET, SOCK_STREAM. \n"); if ((sock_1 = socket (AF_INET, SOCK_STREAM, 0)) == -1){ perror( "socket"); exit(1); } linger.l_onoff = 1; linger.l_linger = 0; setsockopt(sock_1, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); /* * Connect socket 1 to sock2_name. */ printf(" \t Connect socket 1 to sock2_name. \n"); retval = connect(sock_1, (struct sockaddr *)&sock2_name, sizeof (sock2_name)); if (retval){ perror("connect"); cleanup(shut, sock_1); } cleanup(FALSE, sock_1); pause(); }/* end while(1)*/ } /* end main */ $ cc client $ link client $ delete server.c; $ delete server.obj; $ delete client.c; $ delete client.obj; $ @sys$manager:tcpip$define_commands $ a = f$parse("''f$environment(""DEFAULT"")'",,,"DEVICE",)+- f$parse("''f$environment(""DEFAULT"")'",,,"DIRECTORY",) $ server :== $'a'server.exe $ client :== $'a'client.exe $ pipe sysconfig -q inet | search sys$pipe: "tcp_keepcnt" > SYS$SCRATCH:temp.txt $ open/read f SYS$SCRATCH:temp.txt $ read f line $ close f $ delete SYS$SCRATCH:temp.txt; $ value = f$element(1,"=",line) $ value = value - "= " $ tcp_keepcnt = f$integer(value) $ pipe sysconfig -q inet | search sys$pipe: "tcp_keepintvl" > SYS$SCRATCH:temp.txt $ open/read f SYS$SCRATCH:temp.txt $ read f line $ close f $ delete SYS$SCRATCH:temp.txt; $ value = f$element(1,"=",line) $ value = value - "= " $ tcp_keepintvl = f$integer(value) $ fin_wait_2 = tcp_keepintvl*tcp_keepcnt/2 $ spawn/nowait/process=__SERVER server 50000 $ spawn/nowait/process=__CLIENT client localhost 50000 $ write sys$output "Awaiting for tcp_keepintvl*tcp_keepcnt/2 = ''fin_wait_2'"+- " + ''added_fin_wait_2_time' seconds" $ seconds = fin_wait_2 + added_fin_wait_2_time $ minutes = seconds/60 $ hours = minutes/60 $ minutes = minutes - hours*60 $ seconds = seconds - hours*3600 - minutes*60 $ wait 'hours':'minutes':'seconds' $ pipe/priv=auth netstat -an | search sys$pipe: "127.0.0.1.50000" $ stop/image __CLIENT $ stop/image __SERVER $ delete server.exe; $ delete client.exe; $ exit
REFERENCE(S): Related article : ../tima/All-OS-TCPIP-FIN_WAIT_2-CLOSE_WAIT_netstat_-an_discussion.html For TCP/IP states diagram, refer to the following URL: http://tangentsoft.net/wskfaq/articles/debugging-tcp.html