Beispiel #1
0
gint
get_seconds_for_today (void) {

TIME time;
struct tm *tm_today;

    tm_today = get_tm_struct();
    time.hour = tm_today->tm_hour;
    time.minute = tm_today->tm_min;
    time.second = tm_today->tm_sec;

    return time_to_seconds (&time);
}
int main(int argc, char**argv)
{

			//Iterator
		  int i;

		  //Port number
		  int port;

		  //Host file
		  string hostfile;

		  //count of messages to be sent
			int count;

			//Delivery queue
			map<Key,DM*> delivery_queue;

			//Delivered Messages
			vector<Key> delivered_queue;

			//Map to store Hostname to IP address
			map<string,string> host_to_id;

			//Map to store Hostname to id
			map<string,int> hostname_to_id;

			//Map to maintain retransmission info
			map<string,bool> host_retransmit_info;

			//buffer to hold a recieved message
			char *mesg = (char *)malloc(sizeof(AckMessage));

			if(argc < 7)
			{
				kprintf("Usage: ");
				kprintf("proj2 -p port -h hostfile -c count");
				exit(EXIT_FAILURE);
			}

		  for(i=0;i<argc;i++)
		  {
					 string arg = argv[i];
					 if(arg == "-p")
					 {					 			
					 			if(argv[i+1])
								{
									string temp = argv[i+1];
									port = atoi(temp.c_str());
									if(port == 0 || port <=1024)
									{
										kprintf("Invalid port. Please specify a port greater than 1024");
										exit(EXIT_FAILURE);
									}
									i = i + 1;
								}
								else
								{
									kprintf("Please specify port number");
									exit(EXIT_FAILURE);
								}
					 }

					 if(arg == "-h")
					 {	
					 			if(argv[i+1])
								{
									hostfile = argv[i+1];
									i = i + 1;
								}
								else
								{
									kprintf("Please specify host file");
									exit(EXIT_FAILURE);
								}
					 }

					 if(arg == "-c")
					 {
					 			if(argv[i+1])
								{
									string temp = argv[i+1];
									count = atoi(temp.c_str());
									i = i + 1;
									if(count < 0)
									{
										kprintf("Number of messages to be multicast must be greater than zero");
										exit(EXIT_FAILURE);
									}
								}
								else
								{
									kprintf("Please specify number of messages to be multicast");
									exit(EXIT_FAILURE);
								}
					 }

		  }

			kprintf("Port: ",port);
			kprintf("Hostfile: ",hostfile);
			kprintf("Count: ",count);
	
			ifstream hostfile_stream(hostfile.c_str());
		
			string hostname;

			int host_id = 0;

			if(hostfile_stream.is_open())
			{
				while(hostfile_stream.good())
				{		
						getline(hostfile_stream,hostname);		
						if(!hostname.empty())
						{							
							hostname_to_id[hostname] = host_id++;
						
							//Get IP address
							struct hostent *hp;
							hp = gethostbyname(hostname.c_str());
		
							if(!hp)
							{
								kprintf(" not found ",hostname);
								exit(EXIT_FAILURE);			
							}
					
							if((inet_ntoa(*(struct in_addr *)hp->h_addr_list[0])))
							{
									  string s_local(inet_ntoa(*(struct in_addr *)hp->h_addr_list[0]));
	
										kprintf(s_local.c_str());
										
										if(s_local.find("127") != 0)
										{
											host_to_id[hostname] = s_local;
											host_retransmit_info[s_local] = false;
										}
										else
										{
											host_to_id[hostname] = string(inet_ntoa(*(struct in_addr *)hp->h_addr_list[1]));
											host_retransmit_info[string(inet_ntoa(*(struct in_addr *)hp->h_addr_list[1]))] = false;
										}
							}
							else
							{
									host_to_id[hostname] = string(inet_ntoa(*(struct in_addr *)hp->h_addr_list[1]));
									host_retransmit_info[string(inet_ntoa(*(struct in_addr *)hp->h_addr_list[1]))] = false;
							}
							kprintf(hostname.c_str());
	
						}
				}			
				hostfile_stream.close();
			}
			else
			{
				kprintf("Unable to read host file");
				exit(EXIT_FAILURE);
			}

			kprintf("Size is: ",host_to_id.at("xinu01.cs.purdue.edu"));

			//get my hostname
			char myhostname[HOSTNAME_LEN];
			gethostname(myhostname,HOSTNAME_LEN);

			string myhostname_str(myhostname);

			host_retransmit_info[host_to_id[myhostname_str]] = true;

			//Iterator for sending messages		
			int iter=0;

			//Seq numbers to be maintained throughout
			int max_last_proposed_seq_num=0;
			int last_proposed_seq_num = 0;

			//Socket variables
			int sockfd,n;
			struct sockaddr_in servaddr,cliaddr;
			socklen_t lensock;
		
			sockfd = socket(AF_INET,SOCK_DGRAM,0);

			if(sockfd == -1)
			{
				kprintf("Could not create socket");
				exit(EXIT_FAILURE);
			}

			bzero(&servaddr,sizeof(servaddr));
			servaddr.sin_family = AF_INET;
			servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
			servaddr.sin_port=htons(port);
			
			int bval = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
			
			if(bval==-1)
			{
				kprintf("Bind failed\n");
				exit(EXIT_FAILURE);
			}
			
			//Multiset to maintain all the sequence numbers recieved in Acks
			multiset<int> ackmultiset;


			//infinite loop for the protocol
			while(1)
			{
					//Send message to all other hosts/processes only if iter<count
					if(iter<count)
					{
						DataMessage *init_dm = (DataMessage *)malloc(sizeof(DataMessage));
						init_dm->type = TYPE_DM;
						init_dm->sender = hostname_to_id[myhostname_str];
					
						//Get sender ID
						kprintf("Sender ID: ",init_dm->sender);

						init_dm->msg_id = iter;
						init_dm->data = iter;
					
						host_network(init_dm);

						for(map<string,string>::iterator it	=	host_to_id.begin();it	!= host_to_id.end();++it)
						{
							if(host_retransmit_info[it->second.c_str()] == false)
							{
								cliaddr.sin_family = AF_INET;
								cliaddr.sin_addr.s_addr = inet_addr(it->second.c_str());
								cliaddr.sin_port = htons(port);
								int rval = sendto(sockfd,init_dm,sizeof(DataMessage),0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));								
								kprintf("Sent message ",it->second.c_str());
							}
						}					
						
						network_host(init_dm);

						Key current_srch_key (last_proposed_seq_num,hostname_to_id[myhostname_str]);								
				
						//Add the sent message to your queue if it does not already exist
						if(delivery_queue.find(current_srch_key) == delivery_queue.end())
						{
							DM* sent_msg = (DM*)malloc(sizeof(DM));
							sent_msg->deliverable = false;
							sent_msg->seq_num = ++max_last_proposed_seq_num;
							sent_msg->data_msg = init_dm;
							Key current_key (sent_msg->seq_num,hostname_to_id[myhostname_str]);								
							last_proposed_seq_num = max_last_proposed_seq_num;	
							//Add the sent message to your queue
							delivery_queue[current_key] = sent_msg;
							kprintf("Inserted in delivery queue",current_key.seq);
						}
					}

					//Start Recieving messages here
					//start ack timer
					 struct timeval time_ack,time_curr_ack;                            
					 get_now(&time_ack);

					 while(time_to_seconds(&time_ack,get_now(&time_curr_ack)) <= UDP_RETRANSMIT_TIMER)
					 {
					 			
								socklen_t lensock = sizeof(cliaddr);                            
					      if((n = recvfrom(sockfd,mesg,sizeof(AckMessage),MSG_DONTWAIT,(struct sockaddr *)&cliaddr,&lensock)) > 0)
								{
									
									//Get the type by converting from network to host
									uint32_t* recv_type = (uint32_t*)malloc(sizeof(uint32_t));
									memcpy(recv_type,mesg,sizeof(uint32_t));
									int type = *recv_type;
									type = ntohl(type);
			
									kprintf("Recieved Type",type);

									if(type == TYPE_ACK)
									{
												AckMessage* ack_mesg = (AckMessage*)mesg;
												network_host(ack_mesg);
												
												int sender = hostname_to_id[myhostname_str];
												if(ack_mesg->receiver == sender && iter == ack_mesg->msg_id)
												{
													//Mark Ack recieved
													string ip = string(inet_ntoa(cliaddr.sin_addr));
													
													if(host_retransmit_info.find(ip) != host_retransmit_info.end())
													{
														host_retransmit_info[ip] = true;
														kprintf("Marked true for ip ",ip.c_str());
													}
												}
												ackmultiset.insert(ack_mesg->proposed_seq);
									}
									else if(type == TYPE_DM)
									{
										//Check if it is delivered
										DataMessage* dm_mesg = (DataMessage*) mesg;

										network_host(dm_mesg);

										int msg_id = dm_mesg->msg_id;
										int sender = dm_mesg->sender;
										bool delivered = false;

										for(vector<Key>:: iterator it=delivered_queue.begin();it!=delivered_queue.end();++it)
										{
											if((*it).seq == msg_id && (*it).pid == sender)
											{
												delivered = true;
											}
										}
										
										//Check if in ready queue
										bool inreadyqueue = false;

										for(map<Key,DM*>:: iterator it=delivery_queue.begin();it!=delivery_queue.end();it++)
										{
											if((it->second)->data_msg->msg_id == msg_id && (it->second)->data_msg->sender == sender)
												inreadyqueue = true;
										}

										//if not in ready queue or delivered
										if(delivered == false && inreadyqueue == false)
										{
												//Send Ack
												AckMessage* ack_mesg = (AckMessage*)malloc(sizeof(AckMessage));
												ack_mesg->type = TYPE_ACK;
												ack_mesg->sender = hostname_to_id[myhostname_str];
												ack_mesg->msg_id = msg_id;
												ack_mesg->proposed_seq = ++max_last_proposed_seq_num;
												ack_mesg->receiver = sender;
														
												host_network(ack_mesg);		

												//Send ack to the sender
												string ip = string(inet_ntoa(cliaddr.sin_addr));
												cliaddr.sin_family = AF_INET;
												cliaddr.sin_addr.s_addr = inet_addr(ip.c_str());
												cliaddr.sin_port = htons(port);
												int rval = sendto(sockfd,ack_mesg,sizeof(AckMessage),0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));								
												kprintf("Sent Ack to ",ip.c_str());
			
												//Store in Queue ********************
												string current_hostname;

												for(map<string,string>:: iterator it=host_to_id.begin(); it!=host_to_id.end(); it++)
												{
													if((it->second).compare(ip) == 0)
														current_hostname = it->first;
												}
												
												DataMessage *init_dm = (DataMessage *)malloc(sizeof(DataMessage));
												init_dm->type = TYPE_DM;
												init_dm->sender = hostname_to_id[current_hostname];
												init_dm->msg_id = msg_id;
												init_dm->data = dm_mesg->data;											

											
												DM* sent_msg = (DM*)malloc(sizeof(DM));
												sent_msg->deliverable = false;
												sent_msg->seq_num = max_last_proposed_seq_num;
												sent_msg->data_msg = init_dm;
												Key current_key (sent_msg->seq_num,hostname_to_id[current_hostname]);								
												last_proposed_seq_num = max_last_proposed_seq_num;

												//Add the sent message to your queue
												delivery_queue[current_key] = sent_msg;
												kprintf("Inserted in delivery queue",current_key.seq);
										}
									}
									else if(type == TYPE_SEQ_MSG)
									{
												SeqMessage* seq_mesg = (SeqMessage*)mesg;	
												
												network_host(seq_mesg);

												int msg_id = seq_mesg->msg_id;
												int sender = seq_mesg->sender;

												//Search in delivery queue for the corresponding message
												for(map<Key,DM*>:: iterator it=delivery_queue.begin();it!=delivery_queue.end();it++)
												{
													if((it->second)->data_msg->msg_id == msg_id && (it->second)->data_msg->sender == sender)
														{
															(it->second)->deliverable = true;
															(it->second)->seq_num = seq_mesg->final_seq;
														}
												}
												max_last_proposed_seq_num = seq_mesg->final_seq;
												deliver_messages(&delivery_queue,&delivered_queue,hostname_to_id[myhostname_str]);
												kprintf("Got final seq message", seq_mesg->msg_id);

									}						
								}

					 }
												 

					//go for next message
					if(check(host_retransmit_info))
					{
													kprintf("After check");

													//Get the maximum sequence number recieved
													int max = *(ackmultiset.rbegin());

													kprintf("Max is",max);

													//Update the max proposed seq num for next message
													if(max_last_proposed_seq_num < max)
														max_last_proposed_seq_num = max;

													//Build the final seq message
													SeqMessage* final_seq_msg = (SeqMessage *)malloc(sizeof(SeqMessage));
													final_seq_msg->type = TYPE_SEQ_MSG;
													final_seq_msg->sender = hostname_to_id[myhostname_str];
													final_seq_msg->msg_id = iter;
													final_seq_msg->final_seq = max;

													host_network(final_seq_msg);

													//send it to everyone
													for(map<string,string>::iterator it	=	host_to_id.begin();it	!= host_to_id.end();++it)
													{
															if(myhostname_str.compare(it->first) !=0)
															{
																cliaddr.sin_family = AF_INET;
																cliaddr.sin_addr.s_addr = inet_addr(it->second.c_str());
																cliaddr.sin_port = htons(port);
																int rval = sendto(sockfd,final_seq_msg,sizeof(SeqMessage),0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));								
																kprintf("Sent final sequence message ",it->second.c_str());
															}
													}
													
												int msg_id_srch = iter;
												int sender = hostname_to_id[myhostname_str];

												//update the message in delivery queue with the final sequence number
												for(map<Key,DM*>:: iterator it = delivery_queue.begin();it != delivery_queue.end();++it)
												{
														if(it->second->data_msg->sender == sender && it->second->data_msg->msg_id == msg_id_srch)
														{

															//Prepare new message
															DM* updatedDM = (DM*)malloc(sizeof(DM));	
															updatedDM->data_msg = it->second->data_msg;
															updatedDM->deliverable = true;
															updatedDM->seq_num = max;
														
															//erase the old msg
															delivery_queue.erase(it);
															
															//Prepare new key
															Key updated_key(max,sender);

															//Update queue
															delivery_queue[updated_key] = updatedDM;

															kprintf("Found the message and marked deliverable and updated sequence number too",updatedDM->seq_num);

														}
												}
												
					//deliver messages
					deliver_messages(&delivery_queue,&delivered_queue,hostname_to_id[myhostname_str]);
		
					iter++;	
					//Reinitialize for new message
					host_retransmit_info = reinit(host_retransmit_info,host_to_id[myhostname_str]);

					//Reinitialize ack multiset
					ackmultiset = reinit_set(ackmultiset);
				}
	

				
			}

}
Beispiel #3
0
int main( int argc, char **argv ) {
    /* Options with their defaults */
    unsigned short opt_buffer = DEFAULT_BUFFER;
    unsigned short opt_daemon = 0;
    unsigned short opt_debug = 0;
    char *opt_filename = NULL;
    unsigned short opt_port = DEFAULT_PORT;
    unsigned short opt_reply = 0;
    int option;
    /* Program logic */
    unsigned short debug_counter = DEFAULT_LOOPS;
    int client_length;
    int recv_bytes;
    int status;
    static char tcp_options_text[MAX_TCPOPT];
    /* Our process ID and Session ID */
    pid_t pid, sid;

    /* We won't run as root */
   /* if ( geteuid() == 0 ) {
        fprintf(stderr,"This program is not intended to run as superuser.\n");
        fprintf(stderr,"Please use a non-privileged user instead.\n");
        exit(EXIT_FAILURE);
    }*

    /* Parse options */
    while ( (option = getopt(argc, argv, "b:dD:f:hrp:")) != -1 ) {
        switch ( option ) {
            case 'b':
                opt_buffer = (unsigned short)( strtoul( optarg, NULL, 10 ) );
                if ( opt_buffer < MIN_BUFFER ) {
                    opt_buffer = MIN_BUFFER;
                }
                break;
            case 'd':
                opt_daemon = 1;
                break;
            case 'D':
                opt_debug = (unsigned short)( strtoul( optarg, NULL, 10 ) );
                break;
            case 'f':
                opt_filename = optarg;
                break;
            case 'h':
                puts("Welcome to tcpsnoop!\\"
                     "Usage: tcpsnoop [-d] [-D debuglevel] [-f filename] [-h] [-p tcpport] [-b buffersize]");
                exit(EXIT_SUCCESS);
                break;
            case 'p':
                opt_port = (unsigned short)( strtoul( optarg, NULL, 10 ) );
                if ( opt_port < 1024 ) {
                    fprintf(stderr,"We can't bind to port %u! It is privileged.\n",opt_port);
                    exit(EXIT_FAILURE);
                }
                break;
            case 'r':
                opt_reply = 1;
                break;
        }
    }
    if ( opt_filename == NULL ) {
        opt_filename = (char *)default_filename;
    }
    /* Check for debug level. */
    if ( opt_debug > 0 ) {
        printf("Welcome to debug level %d!\n\\"
               "Will listen on port %u and write to file %s.\n\\"
               "Will allocate %u bytes for TCP buffer\n",
                opt_debug,opt_port,opt_filename,opt_buffer);
        /* We don't allow daemon mode when debug mode is active, no 
         * matter whether the daemon option is present or not.
         * */
        opt_daemon = 0;
    }
    if ( opt_daemon != 0 ) {
        syslog( LOG_DAEMON | LOG_INFO, "Starting daemon mode.");
    }

    /* Check if we should go into daemon mode */
    if ( opt_daemon != 0 ) {
        /* Fork and get a PID. */
        pid = fork();
        /* Check if forking was successful */
        if ( pid < 0 ) {
            fprintf(stderr,"fork() failed!\n");
            exit(EXIT_FAILURE);
        }
        if ( pid > 0 ) {
            syslog( LOG_DAEMON | LOG_INFO, "Got PID %u.", pid );
            exit(EXIT_SUCCESS);
        }
    }

    /* Set umask */
    umask(022);

    /* Here we open logs and files we probably need */
    /*
     *
     */
    openlog( SYSLOG_IDENTITY, LOG_PID, LOG_DAEMON );
    statistics = fopen( opt_filename, "a+" );
    if ( statistics == NULL ) {
        if ( opt_debug > 0 ) {
            fprintf(stderr,"Could not open statistics file: %s\n",strerror(errno));
        }
        else {
            syslog( LOG_DAEMON | LOG_CRIT, "Could not open statistics file: %s", strerror(errno) );
            exit(EXIT_FAILURE);
        }
    }

    /* Create a new SID for the child process */
    if ( opt_daemon != 0 ) {
        sid = setsid();
        if (sid < 0) {
            /* Could not aquire new SID. */
            syslog( LOG_DAEMON | LOG_CRIT, "%s", "Could not aquire new SID." );
            exit(EXIT_FAILURE);
        }
        /* Close standard file descriptors since daemons don't do standard
         * I/O. They need to use log files or syslog instead
         */
        fclose(stdout);
        fclose(stdin);
        fclose(stderr);
        set_signal_handlers();
    }

    /* Prepare TCP socket. */
    tcp_socket = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    if ( tcp_socket == -1 ) {
        /* Could not open socket. */
        if ( opt_debug > 0 ) {
            fprintf(stderr,"Could not open TCP socket: %s\n",strerror(errno));
        }
        else {
            syslog( LOG_DAEMON | LOG_CRIT, "Could not open TCP socket: %s", strerror(errno) );
        }
        exit(EXIT_FAILURE);
    }
    else {
        /* Bind to any address on local machine */
        server_address.sin_family = AF_INET;
        server_address.sin_addr.s_addr = inet_addr("10.0.1.2");
        server_address.sin_port = htons(opt_port);
        memset((void *)&(server_address.sin_zero), '\0', 8);
        status = bind( tcp_socket, (struct sockaddr *)&server_address, sizeof(server_address) );
        if ( status == 0 ) {
            /* We can now listen for incoming connections. We only allow a backlog of one
             * connection
             */
            status = listen( tcp_socket, 1 );
            if ( status != 0 ) {
                /* Cannot listen on socket. */
                if ( opt_debug > 0 ) {
                    fprintf(stderr,"Cannot listen on socket: %s\n",strerror(errno));
                }
                else {
                    syslog( LOG_DAEMON | LOG_CRIT, "Cannot listen on socket: %s", strerror(errno) );
                }
                exit(EXIT_FAILURE);
            }
        }
        else {
            /* Cannot bind to socket. */
            if ( opt_debug > 0 ) {
                fprintf(stderr,"Cannot bind to socket: %s\n",strerror(errno));
            }
            else {
                syslog( LOG_DAEMON | LOG_CRIT, "Cannot bind to socket: %s", strerror(errno) );
            }
            exit(EXIT_FAILURE);
        }
    }
    
    /* Allocate Buffer for TCP stream data.
     * (We store it temporarily only since we act as an TCP sink.)
     */
    tcp_buffer = malloc(opt_buffer);
    if ( tcp_buffer == NULL ) {
        if ( opt_debug > 0 ) {
            fprintf(stderr,"Can't allocate buffer for TCP temporary memory.\n");
        }
        else {
            syslog( LOG_DAEMON | LOG_CRIT, "Can't allocate buffer for TCP temporary memory.\n" );
        }
        exit(EXIT_FAILURE);
    }

    /* Our main loop where we wait for (a) TCP connection(s). */
    if ( opt_debug > 0 ) {
        puts("Entering main loop.");
    }
    while ( debug_counter > 0 ) {
        client_length = sizeof(client_address);
        tcp_work_socket = accept( tcp_socket, (struct sockaddr *)&client_address, (socklen_t *)&client_length );
        /* Get time for counting milliseconds. */
        get_now( &time_start, opt_debug );
        /* As soon as we got a connection, we deal with the incoming data by using
         * a second socket. We only read as much as opt_buffer bytes.
         */
        if ( (recv_bytes = recv( tcp_work_socket, tcp_buffer, opt_buffer, 0 ) ) > 0 ) {
            /* Fill tcp_info structure with data to get the TCP options and the client's
             * name.
             */
            tcp_info_length = sizeof(tcp_info);
            if ( getsockopt( tcp_work_socket, SOL_IP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_length ) == 0 ) {
                memset((void *)tcp_options_text, 0, MAX_TCPOPT);
                decode_tcp_options(tcp_options_text,tcp_info.tcpi_options);
                if ( opt_debug > 0 ) {
                    printf("Got a new connection from client %s.\n",inet_ntoa(client_address.sin_addr));
                }
                else {
                    syslog( LOG_DAEMON | LOG_INFO, "Received connection from client at address %s.",
                           inet_ntoa(client_address.sin_addr));
                }
                /* Write some statistics and start of connection to log file. */
                fprintf(statistics,"# Received connection from %s (AdvMSS %u, PMTU %u, options (%0.X): %s)\n",
                        inet_ntoa(client_address.sin_addr),
                        tcp_info.tcpi_advmss,
                        tcp_info.tcpi_pmtu,
                        tcp_info.tcpi_options,
                        tcp_options_text
                       );
            }
        }
        while ( (recv_bytes = recv( tcp_work_socket, tcp_buffer, opt_buffer, 0 ) ) > 0 ) {
            if ( opt_debug > 0 ) {
                printf("\nReceived %d bytes on socket.\n",recv_bytes);
            }
            /* Measure time in order to create time intervals. */
            get_now( &time_now, opt_debug );
            /* Fill tcp_info structure with data */
            tcp_info_length = sizeof(tcp_info);
            if ( getsockopt( tcp_work_socket, SOL_TCP, TCP_INFO, (void *)&tcp_info, (socklen_t *)&tcp_info_length ) == 0 ) {
                if ( opt_debug > 0 ) {
                    printf("snd_cwnd: %u\nsnd_ssthresh: %u\nrcv_ssthresh: %u\nrtt: %u\nrtt_var: %u\n",
                           tcp_info.tcpi_snd_cwnd,
                           tcp_info.tcpi_snd_ssthresh,
                           tcp_info.tcpi_rcv_ssthresh,
                           tcp_info.tcpi_rtt,
                           tcp_info.tcpi_rttvar
                          );
                }
                fprintf(statistics,"%.6f %u %u %u %u %u %u %u %u %u %u %u %u\n",
                        time_to_seconds( &time_start, &time_now ),
                        tcp_info.tcpi_last_data_sent,
                        tcp_info.tcpi_last_data_recv,
                        tcp_info.tcpi_snd_cwnd,
                        tcp_info.tcpi_snd_ssthresh,
                        tcp_info.tcpi_rcv_ssthresh,
                        tcp_info.tcpi_rtt,
                        tcp_info.tcpi_rttvar,
                        tcp_info.tcpi_unacked,
                        tcp_info.tcpi_sacked,
                        tcp_info.tcpi_lost,
                        tcp_info.tcpi_retrans,
                        tcp_info.tcpi_fackets
                       );
                if ( fflush(statistics) != 0 ) {
                    if ( opt_debug > 0 ) {
                        fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno) );
                    }
                    else {
                        syslog( LOG_DAEMON | LOG_CRIT, "Cannot flush buffers: %s", strerror(errno) );
                    }
                }
                /* Send reply text via TCP connection */
                if ( opt_reply != 0 ) {
                    reply_size = snprintf( reply_string, REPLY_MAXLENGTH, "rcv_ssthresh %u\n", tcp_info.tcpi_rcv_ssthresh );
                    if ( reply_size > 0 ) {
                        if ( send( tcp_work_socket, (void *)reply_string, reply_size, MSG_DONTWAIT ) == -1 ) {
                            if ( opt_debug > 0 ) {
                                fprintf(stderr, "Reply size %u didn't match: %s\n", reply_size, strerror(errno) );
                            }
                            else {
                                syslog( LOG_DAEMON | LOG_ERR, "Reply size %u didn't match: %s", reply_size, strerror(errno) );
                            }
                        }
                    }
                }
            }
        }
        close(tcp_work_socket);
        fprintf(statistics,"# Closed connection from %s.\n",inet_ntoa(client_address.sin_addr));
        if ( fflush(statistics) != 0 ) {
            if ( opt_debug > 0 ) {
                fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno) );
            }
            else {
                syslog( LOG_DAEMON | LOG_CRIT, "Cannot flush buffers: %s", strerror(errno) );
            }
        }
        if ( opt_debug > 0 ) {
            debug_counter--;
            printf("Closed connection. Decrementing debug counter to %u.\n\n",debug_counter);
        }
        else {
            syslog( LOG_DAEMON | LOG_INFO, "Closed connection from %s",
                   inet_ntoa(client_address.sin_addr));
        }
        sleep(DEFAULT_SLEEP);
    }

    /* That's a happy ending. */
    exit(EXIT_SUCCESS);
}