Commit f002ead5 authored by Robert Ricci's avatar Robert Ricci

Port to Linux, add a lot more logging, and add a 'single client'

command-line flag.
parent d2deb406
...@@ -14,20 +14,36 @@ ...@@ -14,20 +14,36 @@
* rare) cases. * rare) cases.
* Use non-blocking IO for logging messages * Use non-blocking IO for logging messages
*/ */
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <unistd.h>
#include <stdarg.h> #include <stdarg.h>
#include <netdb.h> #include <netdb.h>
#include <stdlib.h> #include <stdlib.h>
#include <netinet/in.h>
#include <strings.h> #include <strings.h>
#include <string.h>
#include <sys/select.h> #include <sys/select.h>
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <stdbool.h> #include <stdbool.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <sys/time.h>
/*
* These cause Linux to use BSD names in some structures, turn on some more
* stuff in unistd.h, etc.
*/
#ifdef __linux__
#define __PREFER_BSD
#define __USE_BSD
#define __FAVOR_BSD
#endif
#include <netinet/in.h>
#include <unistd.h>
/* /*
* We do huge read()s so that if the machine is busy, and we may have gotten * We do huge read()s so that if the machine is busy, and we may have gotten
...@@ -40,13 +56,19 @@ const int max_readsize = 1024 * 1024; ...@@ -40,13 +56,19 @@ const int max_readsize = 1024 * 1024;
*/ */
const size_t def_readsize = 8192; const size_t def_readsize = 8192;
const uint16_t def_port = 5001; const uint16_t def_port = 5001;
const int def_mss = 0;
const int def_windowsize = 0; const int def_windowsize = 0;
const bool def_daemonize = false; const bool def_daemonize = false;
const bool def_singleclient = false;
const int backlog = 10; const int backlog = 10;
/*
* Internal helper functions
*/
void croak(const char *fmt, ... ); void croak(const char *fmt, ... );
void croak_perror(const char *str); void croak_perror(const char *str);
bool parsenum(char *numstr, int *out);
void fprinttimestamp(FILE *file);
void fprintaddr(FILE *file, struct sockaddr_in *addr);
/* /*
* Command-line options we support, from iperf * Command-line options we support, from iperf
...@@ -54,15 +76,16 @@ void croak_perror(const char *str); ...@@ -54,15 +76,16 @@ void croak_perror(const char *str);
* -l, --len #[KM] length of buffer to read or write (default 8 KB) * -l, --len #[KM] length of buffer to read or write (default 8 KB)
* -p, --port # server port to listen on/connect to * -p, --port # server port to listen on/connect to
* -w, --window #[KM] TCP window size (socket buffer size) * -w, --window #[KM] TCP window size (socket buffer size)
* -B, --bind <host> bind to <host>, an interface or multicast address * -B, --bind <host> bind to <host>, an interface or multicast address
* -C, --compatibility for use with older versions does not sent extra msgs * -C, --compatibility for use with older versions does not sent extra msgs
* -M, --mss # set TCP maximum segment size (MTU - 40 bytes) * -M, --mss # set TCP maximum segment size (MTU - 40 bytes)
* -N, --nodelay set TCP no delay, disabling Nagle's Algorithm * -N, --nodelay set TCP no delay, disabling Nagle's Algorithm
* -D, --daemon run the server as a daemon * -D, --daemon run the server as a daemon
*/ */
#define LONG_OPTIONS() #define LONG_OPTIONS()
const struct option long_options[] = const struct option long_options[] =
{ {
{"single", no_argument, NULL, '1'},
{"len", required_argument, NULL, 'l'}, {"len", required_argument, NULL, 'l'},
{"port", required_argument, NULL, 'p'}, {"port", required_argument, NULL, 'p'},
{"window", required_argument, NULL, 'w'}, {"window", required_argument, NULL, 'w'},
...@@ -71,11 +94,25 @@ const struct option long_options[] = ...@@ -71,11 +94,25 @@ const struct option long_options[] =
{"mss", required_argument, NULL, 'M'}, {"mss", required_argument, NULL, 'M'},
{"nodelay", no_argument, NULL, 'N'}, {"nodelay", no_argument, NULL, 'N'},
{"daemon", no_argument, NULL, 'D'}, {"daemon", no_argument, NULL, 'D'},
{"server", no_argument, NULL, 's'},
{"client", no_argument, NULL, 'c'},
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
int main(int argc, char **argv) { int main(int argc, char **argv) {
printf("iperfd starting\n");
/*
* Print out some hopefully useful debugging information
*/
fprinttimestamp(stdout);
printf(" iperfd starting, called as: ");
for (int i = 0; i<= argc; i++) {
printf(argv[i]);
if (i != argc) {
printf(" ");
}
}
printf("\n");
/* /*
...@@ -83,13 +120,13 @@ int main(int argc, char **argv) { ...@@ -83,13 +120,13 @@ int main(int argc, char **argv) {
*/ */
int readsize = def_readsize; int readsize = def_readsize;
int port = def_port; int port = def_port;
int mss = def_mss;
int windowsize = def_windowsize; int windowsize = def_windowsize;
bool daemonize = def_daemonize; bool daemonize = def_daemonize;
bool singleclient = def_singleclient;
while (1) { while (1) {
char ch; char ch;
int option_index; int option_index;
ch = getopt_long(argc,argv,"hl:p:w:B:CM:ND",long_options, ch = getopt_long(argc,argv,"hl:p:w:B:CM:NDsc1",long_options,
&option_index); &option_index);
if (ch == -1) { if (ch == -1) {
/* End of options */ /* End of options */
...@@ -97,8 +134,10 @@ int main(int argc, char **argv) { ...@@ -97,8 +134,10 @@ int main(int argc, char **argv) {
} }
switch(ch) { switch(ch) {
case 'l': case 'l':
if (sscanf(optarg,"%i",&readsize) != 1) { if (!parsenum(optarg,&readsize)) {
croak("Bad read length parameter\n"); croak("Bad read length parameter\n");
} else if (readsize > max_readsize) {
croak("Read length parameter too big\n");
} }
break; break;
case 'p': case 'p':
...@@ -109,19 +148,24 @@ int main(int argc, char **argv) { ...@@ -109,19 +148,24 @@ int main(int argc, char **argv) {
case 'C': case 'C':
/* No-op */ /* No-op */
break; break;
case 's':
/* No-op */
break;
case 'c':
croak("iperfd cannot be used as a client");
break;
case 'w': case 'w':
if (sscanf(optarg,"%i",&windowsize) != 1) { if (sscanf(optarg,"%i",&windowsize) != 1) {
croak("Bad window size parameter\n"); croak("Bad window size parameter\n");
} }
break; break;
case 'M':
if (sscanf(optarg,"%i",&mss) != 1) {
croak("Bad MSS parameter\n");
}
break;
case 'D': case 'D':
daemonize = true; daemonize = true;
break; break;
case '1':
singleclient = true;
break;
case 'M': /* Just not implemented yet */
case 'N': /* I don't think this one really matters for a server */ case 'N': /* I don't think this one really matters for a server */
case 'B': /* Not likely to actually be used */ case 'B': /* Not likely to actually be used */
croak("Option %c is not yet supported\n",ch); croak("Option %c is not yet supported\n",ch);
...@@ -152,7 +196,9 @@ int main(int argc, char **argv) { ...@@ -152,7 +196,9 @@ int main(int argc, char **argv) {
* Bind to the correct port * Bind to the correct port
*/ */
struct sockaddr_in inaddr; struct sockaddr_in inaddr;
#ifndef __linux__
inaddr.sin_len = sizeof(inaddr); inaddr.sin_len = sizeof(inaddr);
#endif
inaddr.sin_family = htons(PF_INET); inaddr.sin_family = htons(PF_INET);
inaddr.sin_port = htons(port); inaddr.sin_port = htons(port);
inaddr.sin_addr.s_addr = htonl(INADDR_ANY); inaddr.sin_addr.s_addr = htonl(INADDR_ANY);
...@@ -168,6 +214,12 @@ int main(int argc, char **argv) { ...@@ -168,6 +214,12 @@ int main(int argc, char **argv) {
croak_perror("Unable to listen() on socket"); croak_perror("Unable to listen() on socket");
} }
/*
* We'll use this to hold the address of clients that connect, so
* that we can report on which one has disconnected
*/
struct sockaddr_in clients[FD_SETSIZE];
/* /*
* Daemonize if we're supposed to * Daemonize if we're supposed to
*/ */
...@@ -185,6 +237,7 @@ int main(int argc, char **argv) { ...@@ -185,6 +237,7 @@ int main(int argc, char **argv) {
FD_SET(listenfd,&master_fdset); FD_SET(listenfd,&master_fdset);
int maxfd = listenfd; int maxfd = listenfd;
unsigned char buffer[readsize]; unsigned char buffer[readsize];
int client_count = 0;
while (1) { while (1) {
bcopy(&master_fdset, &tmp_fdset, sizeof(master_fdset)); bcopy(&master_fdset, &tmp_fdset, sizeof(master_fdset));
if (select(maxfd + 1, &tmp_fdset, NULL, NULL, NULL) < 0) { if (select(maxfd + 1, &tmp_fdset, NULL, NULL, NULL) < 0) {
...@@ -200,28 +253,36 @@ int main(int argc, char **argv) { ...@@ -200,28 +253,36 @@ int main(int argc, char **argv) {
* Handle the listen socket first * Handle the listen socket first
*/ */
if (FD_ISSET(listenfd, &tmp_fdset)) { if (FD_ISSET(listenfd, &tmp_fdset)) {
/*
* Accept any new clients
*/
struct sockaddr_in clientaddr; struct sockaddr_in clientaddr;
socklen_t clientlen; socklen_t clientlen = sizeof(clientaddr);
int newclient = accept(listenfd, (struct sockaddr *)&clientaddr, int newclient = accept(listenfd, (struct sockaddr *)&clientaddr,
&clientlen); &clientlen);
/* if ((singleclient && (client_count > 0)) ||
* TODO: More interesting logging (newclient >= FD_SETSIZE)) {
*/ /*
if (newclient >= FD_SETSIZE) { * We can only handle so many clients...
fprintf(stderr,"Too many clients, dropping one\n"); */
fprinttimestamp(stdout);
printf(" Too many clients, dropping ");
fprintaddr(stdout,&clientaddr);
printf("\n");
close(newclient); close(newclient);
} else { } else {
printf("Got a new client\n"); client_count++;
fprinttimestamp(stdout);
printf(" New client: ");
fprintaddr(stdout,&clientaddr);
printf("\n");
/* /*
* TODO: Drop client, not croak, on failures here * TODO: Drop client, not croak, on failures here
*/ */
if (mss != 0) {
if (setsockopt(newclient,tcpent->p_proto,TCPOPT_MAXSEG,
&mss,sizeof(mss)) == -1) {
croak_perror("Unable to set MSS for client");
}
}
if (windowsize != 0) { if (windowsize != 0) {
if (setsockopt(newclient,SOL_SOCKET,SO_SNDBUF, if (setsockopt(newclient,SOL_SOCKET,SO_SNDBUF,
&windowsize,sizeof(windowsize)) == -1) { &windowsize,sizeof(windowsize)) == -1) {
...@@ -236,6 +297,7 @@ int main(int argc, char **argv) { ...@@ -236,6 +297,7 @@ int main(int argc, char **argv) {
if (newclient > maxfd) { if (newclient > maxfd) {
maxfd = newclient; maxfd = newclient;
} }
bcopy(&clientaddr, clients + newclient, sizeof(clientaddr));
} }
} }
...@@ -249,11 +311,15 @@ int main(int argc, char **argv) { ...@@ -249,11 +311,15 @@ int main(int argc, char **argv) {
/* /*
* TODO: Do non-blocking I/O and more than one read? * TODO: Do non-blocking I/O and more than one read?
* TODO: Do more reads? (fewer select() calls) * TODO: Do more reads? (fewer select() calls)
* TODO: Handle SIGPIPE? * TODO: Do we need to handle signals, like SIGPIPE?
*/ */
ssize_t readbytes = read(i,buffer,readsize); ssize_t readbytes = read(i,buffer,readsize);
if (readbytes == 0) { if (readbytes == 0) {
printf("Client disconnected\n"); fprinttimestamp(stdout);
printf(" Client disconnected: ");
fprintaddr(stdout,clients + i);
printf("\n");
client_count--;
FD_CLR(i,&master_fdset); FD_CLR(i,&master_fdset);
while (!FD_ISSET(maxfd,&master_fdset)) { while (!FD_ISSET(maxfd,&master_fdset)) {
maxfd--; maxfd--;
...@@ -293,3 +359,48 @@ void croak_perror(const char *str) { ...@@ -293,3 +359,48 @@ void croak_perror(const char *str) {
perror(str); perror(str);
exit(1); exit(1);
} }
/*
* Parse a numeric parameter iperf-style
*/
bool parsenum(char *str, int *out) {
int len = strlen(str);
int multiplier = 1;
if (str[len-1] == 'k') {
multiplier = 1024;
str[len-1] = '\0';
} else if (str[len-1] == 'm') {
multiplier = 1024 * 1024;
str[len-1] = '\0';
}
if (sscanf(str,"%i",out) == 1) {
*out = *out * multiplier;
return true;
} else {
return false;
}
}
/*
* Print the 'seconds' part of the current timestamp
*/
void fprinttimestamp(FILE *file) {
struct timeval time;
if (gettimeofday(&time,NULL) != 0) {
croak_perror("Unable to get time or day!");
}
fprintf(file,"%lu",time.tv_sec);
}
/*
* Print out an internet address in dotted decimal notation, plus port
* number
*/
void fprintaddr(FILE *file, struct sockaddr_in *addr) {
fprintf(file,"%i.%i.%i.%i:%i",
htonl(addr->sin_addr.s_addr) >> 24 & 0xff,
htonl(addr->sin_addr.s_addr) >> 16 & 0xff,
htonl(addr->sin_addr.s_addr) >> 8 & 0xff,
htonl(addr->sin_addr.s_addr) >> 0 & 0xff,
htons(addr->sin_port));
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment