cleanpcap / cnx_inode.c /
23ada98 7 years ago
1 contributor
371 lines | 9.938kb
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <pcap.h>

#include "cnx_inode.h"
#include "inodelist.h"
#include "mypcap.h"

const int MAX_FDLINK = 10;
const int MAX_PID_LENGTH = 20;
#define MAX_SIZE 256

static char st_file_prefix[MAX_SIZE]="";//Will be time of day
static pcap_t *st_hdl_out_pcap = NULL;

const char *cst_cnx_file_details_list[]=
{
	"/proc/net/tcp",
	//~ "/proc/net/tcp6",
	"/proc/net/udp",
	//~ "/proc/net/udp6",
	NULL,
};

bool is_number(const char*s) {
   char* e = NULL;
   uint32_t val = strtol(s, &e, 0);
   return e != NULL && *e == (char)0;
}

void set_file_prefix(const char*file_prefix) {
	strcpy(st_file_prefix,file_prefix);
}
void set_pcap_handle(pcap_t *pcap) {
	st_hdl_out_pcap = pcap;
}

int _file_exist (const char *filename)
{
  struct stat   buffer;   
  return (stat (filename, &buffer) == 0);
}


/*
 * parses a /proc/net/tcp-line of the form:
 *     sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt
 *uid  timeout inode
 *     10: 020310AC:1770 9DD8A9C3:A525 01 00000000:00000000 00:00000000 00000000
 *0        0 2119 1 c0f4f0c0 206 40 10 3 -1
 *     11: 020310AC:0404 936B2ECF:0747 01 00000000:00000000 00:00000000 00000000
 *1000        0 2109 1 c0f4fc00 368 40 20 2 -1
 *
 * and of the form:
 *      2: 0000000000000000FFFF0000020310AC:0016
 *0000000000000000FFFF00009DD8A9C3:A526 01 00000000:00000000 02:000A7214
 *00000000     0        0 2525 2 c732eca0 201 40 1 2 -1
 *
 */
void _addtoconninode(char *buffer) {
	
	short int sa_family;
	struct in6_addr result_addr_local = {};
	struct in6_addr result_addr_remote = {};

	char rem_addr[128], local_addr[128];
	int local_port, rem_port;
	struct in6_addr in6_local;
	struct in6_addr in6_remote;
	unsigned long inode;

	int matches = sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X "
						   "%*X:%*X %*X:%*X %*X %*d %*d %ld %*512s\n",
				   local_addr, &local_port, rem_addr, &rem_port, &inode);

	if (matches != 5) {
		fprintf(stderr, "Unexpected buffer: '%s'\n", buffer);
		exit(0);
	}

	if (inode == 0) {
		/* connection is in TIME_WAIT state. We rely on
		 * the old data still in the table. */
		return;
	}
	//~ printf("inode:%lu\n", inode);

	if (strlen(local_addr) > 8) {
		/* this is an IPv6-style row */

		/* Demangle what the kernel gives us */
		sscanf(local_addr, "%08X%08X%08X%08X", &in6_local.s6_addr32[0],
			   &in6_local.s6_addr32[1], &in6_local.s6_addr32[2],
			   &in6_local.s6_addr32[3]);
		sscanf(rem_addr, "%08X%08X%08X%08X", &in6_remote.s6_addr32[0],
			   &in6_remote.s6_addr32[1], &in6_remote.s6_addr32[2],
			   &in6_remote.s6_addr32[3]);

		if ((in6_local.s6_addr32[0] == 0x0) && (in6_local.s6_addr32[1] == 0x0) &&
			(in6_local.s6_addr32[2] == 0xFFFF0000)) {
		  /* IPv4-compatible address */
		  result_addr_local.s6_addr32[0]  = in6_local.s6_addr32[3];
		  result_addr_remote.s6_addr32[0] = in6_remote.s6_addr32[3];
		  sa_family = AF_INET;
		} else {
		  /* real IPv6 address */
		  // inet_ntop(AF_INET6, &in6_local, addr6, sizeof(addr6));
		  // INET6_getsock(addr6, (struct sockaddr *) &localaddr);
		  // inet_ntop(AF_INET6, &in6_remote, addr6, sizeof(addr6));
		  // INET6_getsock(addr6, (struct sockaddr *) &remaddr);
		  // localaddr.sin6_family = AF_INET6;
		  // remaddr.sin6_family = AF_INET6;
		  result_addr_local = in6_local;
		  result_addr_remote = in6_remote;
		  sa_family = AF_INET6;
		}
	} else {
		/* this is an IPv4-style row */
		sscanf(local_addr, "%X", (unsigned int *)&result_addr_local);
		sscanf(rem_addr, "%X", (unsigned int *)&result_addr_remote);
		sa_family = AF_INET;
	}

	char *hashkey = (char *)malloc(HASHKEYSIZE * sizeof(char));
	char *hashkey2 = (char *)malloc(HASHKEYSIZE * sizeof(char));
	char *local_string = (char *)malloc(50);
	char *remote_string = (char *)malloc(50);
	inet_ntop(sa_family, &result_addr_local, local_string, 49);
	inet_ntop(sa_family, &result_addr_remote, remote_string, 49);

	snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string,
		   local_port, remote_string, rem_port);
	snprintf(hashkey2, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", remote_string,
			rem_port,local_string, local_port);
					 
	free(local_string);

	//~ printf("inode[%lu]:hashkey:[%s]\n",inode,hashkey);
	inodelist_insert_if_not_with_hashkey(inode,hashkey,hashkey2,strlen(hashkey));

	/* workaround: sometimes, when a connection is actually from 172.16.3.1 to
	* 172.16.3.3, packages arrive from 195.169.216.157 to 172.16.3.3, where
	* 172.16.3.1 and 195.169.216.157 are the local addresses of different
	* interfaces */
	//~ for (class local_addr *current_local_addr = local_addrs;
	   //~ current_local_addr != NULL;
	   //~ current_local_addr = current_local_addr->next) {
		//~ /* TODO maybe only add the ones with the same sa_family */
		//~ snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d",
				 //~ current_local_addr->string, local_port, remote_string, rem_port);
		//~ conninode[hashkey] = inode;
	//~ }
	free(hashkey);
	free(remote_string);
}

int _addprocinfo(const char *filename) {
	FILE *procinfo = fopen(filename, "r");
	char buffer[8192];

	if (procinfo == NULL)
		return 0;

	char *ret = fgets(buffer, sizeof(buffer), procinfo);

	do {
		if (fgets(buffer, sizeof(buffer), procinfo))
			_addtoconninode(buffer);
	} while (!feof(procinfo));

	fclose(procinfo);
	return 1;
}

unsigned long _str2ulong(const char *ptr) {
  unsigned long retval = 0;

  while ((*ptr >= '0') && (*ptr <= '9')) {
    retval *= 10;
    retval += *ptr - '0';
    ptr++;
  }
  return retval;
}

int _str2int(const char *ptr) {
  int retval = 0;

  while ((*ptr >= '0') && (*ptr <= '9')) {
    retval *= 10;
    retval += *ptr - '0';
    ptr++;
  }
  return retval;
}

char *_read_file(int fd, char *content) {
	char buf[255];
	int length = 0;
	
	//~ for (length; (length = read(fd, buf, sizeof(buf))) > 0;) {
	while( (length = read(fd, buf, sizeof(buf))) > 0) {
		if (length < 0) {
			fprintf(stderr, "Error reading file: %s\n", strerror(errno));
			exit(34);
		}
		memcpy(content, buf, length);
	}

	return content;
}

char *_read_file_filename(const char *filepath, char *contents) {
  int fd = open(filepath, O_RDONLY);

  if (fd < 0) {
    fprintf(stderr, "Error opening %s: %s\n", filepath,
                 strerror(errno));
    exit(3);
    return NULL;
  }

  _read_file(fd,contents);

  if (close(fd)) {
    fprintf(stderr, "Error opening %s: %s\n", filepath,
                 strerror(errno));
    exit(34);
  }

  return contents;
}

char *_getcmdline(pid_t pid,char *cmdline) {
	const int maxfilenamelen = 14 + MAX_PID_LENGTH + 1;
	char filename[maxfilenamelen];

	snprintf(filename, maxfilenamelen, "/proc/%d/cmdline", pid);

	bool replace_null = false;
	_read_file_filename(filename, cmdline);

	//~ // join parameters, keep prgname separate, don't overwrite trailing null
	size_t idx = 0; 
	for (idx = 0; idx < strlen(cmdline); idx++) {
		if (cmdline[idx] == 0x00) {
			if (replace_null) {
				cmdline[idx] = ' ';
			}
			replace_null = true;
		}
	}
	return cmdline;
}

void _setnode(unsigned long inode, pid_t pid) {
	char cmdline[9000]="";
	_getcmdline(pid,cmdline);
	inodelist_t *tempNodeList=inodelist_get_inode_from_value(inode);
	if( NULL != tempNodeList ) {
		tempNodeList->data.pid = pid;
		if( 0 == strlen(tempNodeList->data.cmdline)) {
			strcpy(tempNodeList->data.cmdline, cmdline);
			
			//PCAP Dumper
			if( NULL == tempNodeList->data.mydumper ) {
				char pcapname[1024];
				sprintf(pcapname,"%s--%d--%s.pcap", st_file_prefix, pid, cmdline );
				uint32_t i = 0;
				for( i = 0; i < strlen(pcapname);i++ ) {
					if( pcapname[i] == '/' )
						pcapname[i]='_';
					else if( pcapname[i] == ':' )
						pcapname[i]='_';
					else if( pcapname[i] == '@' )
						pcapname[i]='_';
					else if( pcapname[i] == ' ' )
						pcapname[i]='_';
				}
				tempNodeList->data.mydumper = mypcap_open( pcapname,"ab+" );
			}
		}
	}
}

void _get_info_by_linkname(const char *pid, const char *linkname) {
	if (strncmp(linkname, "socket:[", 8) == 0) {
		_setnode(_str2ulong(linkname + 8), _str2int(pid));		
	}
}

void _get_info_for_pid(const char *pid) {
	char dirname[10 + MAX_PID_LENGTH];
	size_t dirlen = 10 + strlen(pid);

	snprintf(dirname, dirlen, "/proc/%s/fd", pid);
	DIR *dir = opendir(dirname);
	if (!dir) {
		fprintf(stderr, "Couldn't open dir %s, removing entry\n", dirname);
		int32_t failed_index = inodelist_get_index_from_pid(_str2int(pid));
		if( -1 != failed_index ) {
			inodelist_remove_at(failed_index);
		}
		return;
	}

	/* walk through /proc/%s/fd/... */
	struct dirent *entry;
	while ((entry = readdir(dir))) {
		if (entry->d_type != DT_LNK)
			continue;

		size_t fromlen = dirlen + strlen(entry->d_name) + 1;
		char fromname[10 + MAX_PID_LENGTH + 1 + MAX_FDLINK];
		snprintf(fromname, fromlen, "%s/%s", dirname, entry->d_name);

		int linklen = 80;
		char linkname[linklen];
		int usedlen = readlink(fromname, linkname, linklen - 1);
		if (usedlen == -1) {
			continue;
		}
		assert(usedlen < linklen);
		linkname[usedlen] = '\0';
		_get_info_by_linkname(pid, linkname);
	}
	closedir(dir);
}


void cnx_inode_refresh() {
	uint32_t i = 0;
	while(NULL != cst_cnx_file_details_list[i]) {
		if (!_addprocinfo(cst_cnx_file_details_list[i])) {
			fprintf(stderr, "Error: couldn't open %s", cst_cnx_file_details_list[i] );
		}
		i++;
	}
}

void reread_mapping() {
	DIR *proc = opendir("/proc");

	if (proc == 0) {
		fprintf(stderr, "Error reading /proc, needed to get inode-to-pid-maping\n");
		exit(1);
	}

	struct dirent *entry;
	while ((entry = readdir(proc))) {
		if (entry->d_type != DT_DIR)
			continue;

		if (!is_number(entry->d_name))
			continue;
		//~ printf("/proc/%s\n", entry->d_name );
		_get_info_for_pid(entry->d_name);
	}
	closedir(proc);
}