Commit fe86dd4e authored by Robert Ricci's avatar Robert Ricci

Add two new features to libnetmon. First, it now reports on the

line of the application it's instrumenting, so that we can find the
source of some mysterious lines in the libnetmon.out files. Second,
report when sendmsg gets called, so that we can find out whether we
need to instrument this for any of our applications or not.

I also fixed several minor almost-bugs caught by -pedantic.
parent 3e0890fe
...@@ -13,5 +13,6 @@ if [ "x$LIBNETMON_OUTPUTVERSION" = "x" ]; then ...@@ -13,5 +13,6 @@ if [ "x$LIBNETMON_OUTPUTVERSION" = "x" ]; then
else else
export LIBNETMON_OUTPUTVERSION export LIBNETMON_OUTPUTVERSION
fi fi
#export LIBNETMON_NETMOND=1
$* $*
...@@ -26,6 +26,9 @@ Connected (no value) (RemoteIP and RemotePort MUST be sent before this command) ...@@ -26,6 +26,9 @@ Connected (no value) (RemoteIP and RemotePort MUST be sent before this command)
Send (value is the size of the write or sendto) Send (value is the size of the write or sendto)
SendTo (value is <localPort>:<remoteIP>:<remotePort>:<size> SendTo (value is <localPort>:<remoteIP>:<remotePort>:<size>
Closed (no value) Closed (no value)
Init (value is command line, enclosed in ' ')
Exit (no value)
SendMsg (no value yet)
On a TCP connection, there should be the following sequence: On a TCP connection, there should be the following sequence:
......
/* /*
* EMULAB-COPYRIGHT * EMULAB-COPYRIGHT
* Copyright (c) 2006 University of Utah and the Flux Group. * Copyright (c) 2006-2007 University of Utah and the Flux Group.
* *
* libnetmon, a library for monitoring network traffic sent by a process. See * libnetmon, a library for monitoring network traffic sent by a process. See
* README for instructions. * README for instructions.
*/ */
#include "libnetmon.h" #include "libnetmon.h"
/*
* So that we can find out the name of the process that we're instrumenting
*/
extern int argc;
extern char** argv;
/* /*
* Die with a standard Emulab-type error message format. In the future, I might * Die with a standard Emulab-type error message format. In the future, I might
* try to modify this to simply unlink our wrapper functions so that the app * try to modify this to simply unlink our wrapper functions so that the app
...@@ -28,13 +34,13 @@ static void croak(const char *format, ...) { ...@@ -28,13 +34,13 @@ static void croak(const char *format, ...) {
*/ */
void lnm_init() { void lnm_init() {
static bool intialized = false; static bool initialized = false;
char *sockpath; char *sockpath;
char *ctrl_sockpath; char *ctrl_sockpath;
char *filepath; char *filepath;
char *std_netmond; char *std_netmond;
if (intialized == false) { if (initialized == false) {
DEBUG(printf("Initializing\n")); DEBUG(printf("Initializing\n"));
/* /*
...@@ -56,7 +62,7 @@ void lnm_init() { ...@@ -56,7 +62,7 @@ void lnm_init() {
char *outversion_s; char *outversion_s;
outversion_s = getenv("LIBNETMON_OUTPUTVERSION"); outversion_s = getenv("LIBNETMON_OUTPUTVERSION");
if (outversion_s) { if (outversion_s) {
if (!sscanf(outversion_s,"%i",&output_version) == 1) { if (!sscanf(outversion_s,"%u",&output_version) == 1) {
croak("Bad output version: %s\n",outversion_s); croak("Bad output version: %s\n",outversion_s);
} }
} else { } else {
...@@ -94,7 +100,7 @@ void lnm_init() { ...@@ -94,7 +100,7 @@ void lnm_init() {
* Default to reporting on everything - the individual reporting * Default to reporting on everything - the individual reporting
* options will get turned on later if report_all is true * options will get turned on later if report_all is true
*/ */
report_io = report_sockopt = report_connect = false; report_io = report_sockopt = report_connect = report_init = false;
report_all = true; report_all = true;
char *reports_s; char *reports_s;
reports_s = getenv("LIBNETMON_REPORTS"); reports_s = getenv("LIBNETMON_REPORTS");
...@@ -122,6 +128,7 @@ void lnm_init() { ...@@ -122,6 +128,7 @@ void lnm_init() {
FIND_REAL_FUN(recvmsg); FIND_REAL_FUN(recvmsg);
FIND_REAL_FUN(accept); FIND_REAL_FUN(accept);
FIND_REAL_FUN(sendto); FIND_REAL_FUN(sendto);
FIND_REAL_FUN(sendmsg);
/* /*
* Connect to netmond if we've been asked to * Connect to netmond if we've been asked to
...@@ -247,7 +254,7 @@ void lnm_init() { ...@@ -247,7 +254,7 @@ void lnm_init() {
* others now * others now
*/ */
if (report_all) { if (report_all) {
report_io = report_sockopt = report_connect = true; report_io = report_sockopt = report_connect = report_init = true;
} }
/* /*
...@@ -273,7 +280,51 @@ void lnm_init() { ...@@ -273,7 +280,51 @@ void lnm_init() {
croak("Unable to register atexit() function\n"); croak("Unable to register atexit() function\n");
} }
intialized = true; /*
* Get our command line. I really don't like going into the /proc
* filesystem to get it, but it beats mucking around in the stack.
*/
char cmdfile[1024];
char *cmdline;
char cmdbuf[4096];
snprintf(cmdfile,1024,"/proc/%i/cmdline",pid);
DEBUG(printf("Opening %s\n",cmdfile));
int cmdfd = open(cmdfile,O_RDONLY);
/*
* If we couldn't open it, report why in lieu of the command line
*/
if (cmdfd < 0) {
cmdline = strerror(errno);
} else {
/*
* Read as much of the command line as we can, and null terminate
* it
*/
int bytesread = real_read(cmdfd,cmdbuf,sizeof(cmdbuf) - 1);
real_close(cmdfd);
cmdbuf[bytesread] = '\0';
/*
* But wait, there's more! What we got out of /proc is
* null-separated, so we have to change it into a tidy
* space-separated string.
*/
for (int i = 0; i < bytesread - 1; i++) {
if (cmdbuf[i] == '\0') {
cmdbuf[i] = ' ';
}
}
cmdline = cmdbuf;
}
/*
* XXX: Should escape 's, so as not to confuse the monitor
*/
printlog(LOG_INIT,NO_FD,"'%s'",cmdline);
initialized = true;
DEBUG(printf("Done initializing\n"));
} else { } else {
/* DEBUG(printf("Skipping intialization\n")); */ /* DEBUG(printf("Skipping intialization\n")); */
} }
...@@ -336,9 +387,9 @@ void lnm_control_wait() { ...@@ -336,9 +387,9 @@ void lnm_control_wait() {
DEBUG(printf("Waiting for a control message\n")); DEBUG(printf("Waiting for a control message\n"));
selectrv = select(controlfd + 1, &fds, NULL, NULL, &tv); selectrv = select(controlfd + 1, &fds, NULL, NULL, &tv);
if (select == 0) { if (selectrv == 0) {
croak("Timed out waiting for a control message\n"); croak("Timed out waiting for a control message\n");
} else if (select < 0) { } else if (selectrv < 0) {
croak("Bad return value from select() in lnm_control_wait()\n"); croak("Bad return value from select() in lnm_control_wait()\n");
} }
...@@ -525,6 +576,7 @@ void stopWatchingAll() { ...@@ -525,6 +576,7 @@ void stopWatchingAll() {
stopFD(i); stopFD(i);
} }
} }
printlog(LOG_EXIT,NO_FD);
} }
void printlog(logmsg_t type, int fd, ...) { void printlog(logmsg_t type, int fd, ...) {
...@@ -534,6 +586,7 @@ void printlog(logmsg_t type, int fd, ...) { ...@@ -534,6 +586,7 @@ void printlog(logmsg_t type, int fd, ...) {
*/ */
bool print_name = true; bool print_name = true;
bool print_id = true; bool print_id = true;
bool id_is_pid = false;
bool print_timestamp = true; bool print_timestamp = true;
bool print_value = true; bool print_value = true;
...@@ -627,6 +680,25 @@ void printlog(logmsg_t type, int fd, ...) { ...@@ -627,6 +680,25 @@ void printlog(logmsg_t type, int fd, ...) {
// Allow global turning on/off of this message type // Allow global turning on/off of this message type
if (!report_connect) { print = false; } if (!report_connect) { print = false; }
break; break;
case LOG_INIT:
if (output_version < 3) { print = false; }
if (!report_init) { print = false; }
id_is_pid = true;
break;
case LOG_EXIT:
if (output_version < 3) { print = false; }
if (!report_init) { print = false; }
id_is_pid = true;
print_value = false;
break;
case LOG_SENDMSG:
// In version 3, we don't actually log anything about the contents
// of the call - we just want to make sure it isn't sneaking past
// us. Techinically, it doesn't have to be UDP, but we'll lump it
// in for now
if (output_version < 3) { print = false; }
if (!monitor_udp) { print = false; }
print_value = false;
default: default:
croak("Invalid type (%) passed to printlog()\n",type); croak("Invalid type (%) passed to printlog()\n",type);
} }
...@@ -647,11 +719,16 @@ void printlog(logmsg_t type, int fd, ...) { ...@@ -647,11 +719,16 @@ void printlog(logmsg_t type, int fd, ...) {
} }
/* /*
* Print out the ID of the socket the action is for * Print out the ID of the socket the action is for. Some actions are not
* associated with a specific socket, so we use the pid as the identifier
*/ */
if (print_id) { if (print_id) {
fprintID(outstream,fd); if (id_is_pid) {
fprintf(outstream," "); fprintf(outstream,"%i ",pid);
} else {
fprintID(outstream,fd);
fprintf(outstream," ");
}
} }
/* /*
...@@ -682,6 +759,12 @@ void printlog(logmsg_t type, int fd, ...) { ...@@ -682,6 +759,12 @@ void printlog(logmsg_t type, int fd, ...) {
*/ */
void fprintID(FILE *f, int fd) { void fprintID(FILE *f, int fd) {
if (fd == NO_FD) {
croak("NO_FD passed to fprintID");
} else if (fd < 0) {
croak("Negative pid (%i) passed to fprintID");
}
if (output_version <= 2) { if (output_version <= 2) {
/* /*
* Note, we've switched from local_port to FD for the first field - this is * Note, we've switched from local_port to FD for the first field - this is
...@@ -857,6 +940,8 @@ static void lnm_parse_reportopt(char *s) { ...@@ -857,6 +940,8 @@ static void lnm_parse_reportopt(char *s) {
report_connect = true; report_connect = true;
} else if (!strncmp(p,"sockopt",numchars)) { } else if (!strncmp(p,"sockopt",numchars)) {
report_sockopt = true; report_sockopt = true;
} else if (!strncmp(p,"init",numchars)) {
report_init = true;
} else { } else {
croak("Bad report: %s\n",p); croak("Bad report: %s\n",p);
} }
...@@ -1062,8 +1147,8 @@ void informConnect(int fd) { ...@@ -1062,8 +1147,8 @@ void informConnect(int fd) {
} }
int getNewSockbuf(int fd, int which) { int getNewSockbuf(int fd, int which) {
int newsize; socklen_t newsize;
int optsize; socklen_t optsize;
optsize = sizeof(newsize); optsize = sizeof(newsize);
if (getsockopt(fd,SOL_SOCKET,which,&newsize,&optsize)) { if (getsockopt(fd,SOL_SOCKET,which,&newsize,&optsize)) {
croak("Unable to get socket buffer size"); croak("Unable to get socket buffer size");
...@@ -1160,7 +1245,7 @@ int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) { ...@@ -1160,7 +1245,7 @@ int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) {
* Get the local port number so that we can monitor it * Get the local port number so that we can monitor it
*/ */
struct sockaddr_in localaddr; struct sockaddr_in localaddr;
int namelen = sizeof(localaddr); socklen_t namelen = sizeof(localaddr);
if (getsockname(sockfd, (struct sockaddr*)&localaddr,&namelen) != 0) { if (getsockname(sockfd, (struct sockaddr*)&localaddr,&namelen) != 0) {
croak("Unable to get local socket name: %s\n", strerror(errno)); croak("Unable to get local socket name: %s\n", strerror(errno));
} }
...@@ -1244,7 +1329,6 @@ int close(int d) { ...@@ -1244,7 +1329,6 @@ int close(int d) {
* *
* TODO: Need to write wrappers for other functions that can be used to send * TODO: Need to write wrappers for other functions that can be used to send
* data on a socket: * data on a socket:
* sendmsg
* writev * writev
* others? * others?
*/ */
...@@ -1340,6 +1424,30 @@ ssize_t sendto(int fd, const void *buf, size_t count, int flags, ...@@ -1340,6 +1424,30 @@ ssize_t sendto(int fd, const void *buf, size_t count, int flags,
} }
/*
* Wrap the sendmsg() function
*/
ssize_t sendmsg(int s, const struct msghdr *msg, int flags) {
ssize_t rv;
lnm_init();
lnm_control();
/*
* Wait until _after_ the packet is sent to log it, since the call might
* block, and we basically want to report when the kernel acked receipt of
* the packet
*/
rv = real_sendmsg(s,msg,flags);
if ((rv > 0) && monitorFD_p(s)) {
printlog(LOG_SENDMSG,s);
}
return rv;
}
int setsockopt (int s, int level, int optname, const void *optval, int setsockopt (int s, int level, int optname, const void *optval,
socklen_t optlen) { socklen_t optlen) {
int rv; int rv;
......
/* /*
* EMULAB-COPYRIGHT * EMULAB-COPYRIGHT
* Copyright (c) 2006 University of Utah and the Flux Group. * Copyright (c) 2006-2007 University of Utah and the Flux Group.
* *
* Header for libnetmon, a library for monitoring network traffic sent by a * Header for libnetmon, a library for monitoring network traffic sent by a
* process. See README for instructions. * process. See README for instructions.
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include "netmon.h" #include "netmon.h"
/* #define DEBUGGING */ // #define DEBUGGING
#ifdef DEBUGGING #ifdef DEBUGGING
#define DEBUG(x) (x) #define DEBUG(x) (x)
...@@ -107,7 +107,10 @@ typedef enum { LOG_NEW = 0, ...@@ -107,7 +107,10 @@ typedef enum { LOG_NEW = 0,
LOG_CONNECTED, LOG_CONNECTED,
LOG_SEND, LOG_SEND,
LOG_SENDTO, LOG_SENDTO,
LOG_CLOSED LOG_CLOSED,
LOG_INIT,
LOG_EXIT,
LOG_SENDMSG
} logmsg_t; } logmsg_t;
static char *log_type_names[] = { static char *log_type_names[] = {
"New", "New",
...@@ -121,7 +124,10 @@ static char *log_type_names[] = { ...@@ -121,7 +124,10 @@ static char *log_type_names[] = {
"Connected", "Connected",
"Send", "Send",
"SendTo", "SendTo",
"Closed" "Closed",
"Init",
"Exit",
"SendMsg"
}; };
/* /*
...@@ -132,6 +138,12 @@ static char *log_type_names[] = { ...@@ -132,6 +138,12 @@ static char *log_type_names[] = {
*/ */
static void printlog(logmsg_t,int, ... ); static void printlog(logmsg_t,int, ... );
/*
* A constant passed to printlog() to indicate that there is no file descriptor
* associated with this message
*/
static int NO_FD = -42;
/* /*
* Log that a packet has been sent to the kernel on a given FD with a given * Log that a packet has been sent to the kernel on a given FD with a given
* size. If the final argument is non-NULL, then we are logging a packet from * size. If the final argument is non-NULL, then we are logging a packet from
...@@ -254,6 +266,7 @@ static bool report_all; ...@@ -254,6 +266,7 @@ static bool report_all;
static bool report_io; static bool report_io;
static bool report_sockopt; static bool report_sockopt;
static bool report_connect; static bool report_connect;
static bool report_init;
/* /*
* Prototypes for the real library functions - just makes it easier to declare * Prototypes for the real library functions - just makes it easier to declare
...@@ -261,7 +274,7 @@ static bool report_connect; ...@@ -261,7 +274,7 @@ static bool report_connect;
*/ */
typedef int open_proto_t(const char *, int, ...); typedef int open_proto_t(const char *, int, ...);
typedef int stat_proto_t(const char *, struct stat *sb); typedef int stat_proto_t(const char *, struct stat *sb);
typedef int socket_proto_t(int,int,int); typedef int socket_proto_t(int, int,int);
typedef int close_proto_t(int); typedef int close_proto_t(int);
typedef int connect_proto_t(int, const struct sockaddr*, socklen_t); typedef int connect_proto_t(int, const struct sockaddr*, socklen_t);
typedef ssize_t write_proto_t(int, const void *, size_t); typedef ssize_t write_proto_t(int, const void *, size_t);
...@@ -269,10 +282,11 @@ typedef ssize_t send_proto_t(int, const void *, ssize_t, int); ...@@ -269,10 +282,11 @@ typedef ssize_t send_proto_t(int, const void *, ssize_t, int);
typedef int setsockopt_proto_t(int, int, int, const void*, socklen_t); typedef int setsockopt_proto_t(int, int, int, const void*, socklen_t);
typedef ssize_t read_proto_t(int, void *, size_t); typedef ssize_t read_proto_t(int, void *, size_t);
typedef ssize_t recv_proto_t(int, void *, size_t, int); typedef ssize_t recv_proto_t(int, void *, size_t, int);
typedef ssize_t recvmsg_proto_t(int,struct msghdr *, int); typedef ssize_t recvmsg_proto_t(int, struct msghdr *, int);
typedef ssize_t accept_proto_t(int,struct sockaddr *, socklen_t *); typedef ssize_t accept_proto_t(int, struct sockaddr *, socklen_t *);
typedef ssize_t sendto_proto_t(int, const void *, size_t, int, typedef ssize_t sendto_proto_t(int, const void *, size_t, int,
const struct sockaddr *, socklen_t); const struct sockaddr *, socklen_t);
typedef ssize_t sendmsg_proto_t(int, const struct msghdr *, int);
/* /*
* Locations of the real library functions * Locations of the real library functions
...@@ -288,6 +302,7 @@ static recv_proto_t *real_recv; ...@@ -288,6 +302,7 @@ static recv_proto_t *real_recv;
static recvmsg_proto_t *real_recvmsg; static recvmsg_proto_t *real_recvmsg;
static accept_proto_t *real_accept; static accept_proto_t *real_accept;
static sendto_proto_t *real_sendto; static sendto_proto_t *real_sendto;
static sendmsg_proto_t *real_sendmsg;
/* /*
* Note: Functions that we're wrapping are in the .c file * Note: Functions that we're wrapping are in the .c file
......
...@@ -43,7 +43,7 @@ typedef enum { ...@@ -43,7 +43,7 @@ typedef enum {
*/ */
typedef struct { typedef struct {
unsigned int type; unsigned int type;
unsigned char payload[CONTROL_MESSAGE_PAYLOAD_SIZE]; char payload[CONTROL_MESSAGE_PAYLOAD_SIZE];
} generic_m; } generic_m;
...@@ -78,7 +78,7 @@ typedef struct { ...@@ -78,7 +78,7 @@ typedef struct {
*/ */
typedef struct { typedef struct {
unsigned int type; unsigned int type;
unsigned char reports[CONTROL_MESSAGE_PAYLOAD_SIZE]; char reports[CONTROL_MESSAGE_PAYLOAD_SIZE];
} reports_m; } reports_m;
/* /*
......
/* /*
* EMULAB-COPYRIGHT * EMULAB-COPYRIGHT
* Copyright (c) 2006 University of Utah and the Flux Group. * Copyright (c) 2006-2007 University of Utah and the Flux Group.
* *
* netmond, a 'server' for libnetmon - simply repeat what a process being * netmond, a 'server' for libnetmon - simply repeat what a process being
* monitored with libnetmon tell us on a unix-domian socket * monitored with libnetmon tell us on a unix-domian socket
...@@ -173,17 +173,17 @@ int main(int argc, char **argv) { ...@@ -173,17 +173,17 @@ int main(int argc, char **argv) {
while ((ch = getopt(argc, argv, "f:l:v:r:u")) != -1) { while ((ch = getopt(argc, argv, "f:l:v:r:u")) != -1) {
switch(ch) { switch(ch) {
case 'f': case 'f':
if (sscanf(optarg,"%i",&forced_socksize) != 1) { if (sscanf(optarg,"%u",&forced_socksize) != 1) {
usage(); usage();
} }
break; break;
case 'l': case 'l':
if (sscanf(optarg,"%i",&max_socksize) != 1) { if (sscanf(optarg,"%u",&max_socksize) != 1) {
usage(); usage();
} }
break; break;
case 'v': case 'v':
if (sscanf(optarg,"%i",&output_version) != 1) { if (sscanf(optarg,"%u",&output_version) != 1) {
usage(); usage();
} }
break; break;
......
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