diff --git a/hashtbl.cpp b/hashtbl.cpp index a5222fc..99ff58f 100644 --- a/hashtbl.cpp +++ b/hashtbl.cpp @@ -14,7 +14,6 @@ HashNode::~HashNode () HashTable::HashTable(int n_size) { - // size = n_size; // TODO allow for variable size size = n_size; table = (HashNode **) malloc (size * sizeof(HashNode *)); diff --git a/nethogs.cpp b/nethogs.cpp index 157aec1..c7dc064 100644 --- a/nethogs.cpp +++ b/nethogs.cpp @@ -34,13 +34,40 @@ timeval curtime; std::string * caption; bool local_addr::contains (const in_addr_t & n_addr) { - if (n_addr == addr) + if ((sa_family == AF_INET) + && (n_addr == addr)) return true; if (next == NULL) return false; return next->contains(n_addr); } +bool local_addr::contains(const struct in6_addr & n_addr) { + if (sa_family == AF_INET6) + { + /* + if (DEBUG) { + char addy [50]; + std::cerr << "Comparing: "; + inet_ntop (AF_INET6, &n_addr, addy, 49); + std::cerr << addy << " and "; + inet_ntop (AF_INET6, &addr6, addy, 49); + std::cerr << addy << std::endl; + } + */ + //if (addr6.s6_addr == n_addr.s6_addr) + if (memcmp (&addr6, &n_addr, sizeof(struct in6_addr)) == 0) + { + if (DEBUG) + std::cerr << "Match!" << std::endl; + return true; + } + } + if (next == NULL) + return false; + return next->contains(n_addr); +} + void process (u_char * args, const struct pcap_pkthdr * header, const u_char * m_packet) { curtime = header->ts; @@ -94,11 +121,12 @@ static void versiondisplay(void) static void help(void) { - std::cerr << "usage: nethogs [-V] [-d] [device]\n"; + std::cerr << "usage: nethogs [-V] [-d] [device [device [device ...]]]\n"; std::cerr << " -V : prints version.\n"; std::cerr << " -d : delay for update refresh rate in seconds. default is 1.\n"; std::cerr << " -t : tracemode.\n"; - std::cerr << " device : device to monitor. default is eth0\n"; + std::cerr << " -p : sniff in promiscious mode (not recommended).\n"; + std::cerr << " device : device(s) to monitor. default is eth0\n"; } class device { diff --git a/nethogs.h b/nethogs.h index 5cffe48..f480d79 100644 --- a/nethogs.h +++ b/nethogs.h @@ -2,6 +2,11 @@ #define __NETHOGS_H #include +#include +#include +#include +#include +#include #define _BSD_SOURCE 1 @@ -19,8 +24,12 @@ #define NEEDROOT 1 #endif -#define DEBUG 1 +#define DEBUG 0 +// 2 times: 32 characters, 7 ':''s, a ':12345'. +// 1 '-' +// -> 2*45+1=91. we make it 92, for the null. +#define HASHKEYSIZE 92 #define PROGNAME_WIDTH 27 @@ -28,14 +37,60 @@ void forceExit(const char *msg); class local_addr { public: + /* ipv4 constructor takes an in_addr_t */ local_addr (in_addr_t m_addr, local_addr * m_next = NULL) { addr = m_addr; next = m_next; + sa_family = AF_INET; } + /* this constructor takes an char address[33] */ + local_addr (char m_address [33], local_addr * m_next = NULL) + { + next = m_next; + char address [40]; + address[0] = m_address[0]; address[1] = m_address[1]; + address[2] = m_address[2]; address[3] = m_address[3]; + address[4] = ':'; + address[5] = m_address[4]; address[6] = m_address[5]; + address[7] = m_address[6]; address[8] = m_address[7]; + address[9] = ':'; + address[10] = m_address[8]; address[11] = m_address[9]; + address[12] = m_address[10]; address[13] = m_address[11]; + address[14] = ':'; + address[15] = m_address[12]; address[16] = m_address[13]; + address[17] = m_address[14]; address[18] = m_address[15]; + address[19] = ':'; + address[20] = m_address[16]; address[21] = m_address[17]; + address[22] = m_address[18]; address[23] = m_address[19]; + address[24] = ':'; + address[25] = m_address[20]; address[26] = m_address[21]; + address[27] = m_address[22]; address[28] = m_address[23]; + address[29] = ':'; + address[30] = m_address[24]; address[31] = m_address[25]; + address[32] = m_address[26]; address[33] = m_address[27]; + address[34] = ':'; + address[35] = m_address[28]; address[36] = m_address[29]; + address[37] = m_address[30]; address[38] = m_address[31]; + address[39] = 0; + if (DEBUG) + std::cout << "Converting address " << address << std::endl; + + int result = inet_pton (AF_INET6, address, &addr6); + + if (DEBUG) + assert (result > 0); + sa_family = AF_INET6; + } + bool contains (const in_addr_t & n_addr); + bool contains (const struct in6_addr & n_addr); +private: + in_addr_t addr; + struct in6_addr addr6; local_addr * next; + short int sa_family; }; #endif diff --git a/packet.cpp b/packet.cpp index 3f4b194..099be7d 100644 --- a/packet.cpp +++ b/packet.cpp @@ -14,6 +14,15 @@ local_addr * local_addrs = NULL; +/* moves the pointer right until a non-space is seen */ +char * stripspaces (char * input) +{ + char * retval = input; + while (*retval == ' ') + retval++; + return retval; +} + /* * getLocal * device: This should be device explicit (e.g. eth0:1) @@ -23,6 +32,7 @@ local_addr * local_addrs = NULL; */ void getLocal (const char *device) { + /* get local IPv4 addresses */ int sock; struct ifreq iFreq; struct sockaddr_in *saddr; @@ -36,44 +46,43 @@ void getLocal (const char *device) } saddr=(struct sockaddr_in*)&iFreq.ifr_addr; local_addrs = new local_addr (saddr->sin_addr.s_addr, local_addrs); + + /* also get local IPv6 addresses */ + FILE * ifinfo = fopen ("/proc/net/if_inet6", "r"); + char buffer [500]; + if (ifinfo) + { + do + { + if (fgets(buffer, sizeof(buffer), ifinfo)) + { + char address [33]; + char ifname [9]; + int n_results = sscanf (buffer, "%32[0-9a-f] %*d %*d %*d %*d %8[0-9a-zA-Z]", address, ifname); + if (DEBUG) + assert (n_results = 2); + + if (strcmp (stripspaces(ifname), device) == 0) + { + local_addrs = new local_addr (address, local_addrs); + } +#if DEBUG + else + { + std::cerr << "Address skipped for interface " << ifname << std::endl; + } +#endif + } + } while (!feof(ifinfo)); + fclose(ifinfo); + } } typedef u_int32_t tcp_seq; -/* ethernet header (now unused) */ -/*struct ethernet_hdr { - u_char ether_dhost[ETHER_ADDR_LEN]; - u_char ether_shost[ETHER_ADDR_LEN]; - u_short ether_type; -};*/ - -/* IP header */ -struct ip_hdr -{ -#if BYTE_ORDER == LITTLE_ENDIAN - u_int ip_hl:4, /* header length */ - ip_v:4; /* version */ -#if BYTE_ORDER == BIG_ENDIAN - u_int ip_v:4, /* version */ - ip_hl:4; /* header length */ -#endif -#endif /* not _IP_VHL */ - u_char ip_tos; /* type of service */ - u_short ip_len; /* total length */ - u_short ip_id; /* identification */ - u_short ip_off; /* fragment offset field */ -#define IP_RF 0x8000 /* reserved fragment flag */ -#define IP_DF 0x4000 /* dont fragment flag */ -#define IP_MF 0x2000 /* more fragments flag */ -#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ - u_char ip_ttl; /* time to live */ - u_char ip_p; /* protocol */ - u_short ip_sum; /* checksum */ - struct in_addr ip_src,ip_dst; /* source and dest address */ -}; - -/* TCP header */ -struct tcp_hdr { +/* TCP header */ +// TODO take from elsewhere. +struct tcp_hdr { u_short th_sport; /* source port */ u_short th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ @@ -104,13 +113,12 @@ struct tcp_hdr { /* Packet 'Constructor' - but returns NULL on failure */ Packet * getPacket (const struct pcap_pkthdr * header, const u_char * packet) { - //const struct ethernet_hdr * ethernet = (struct ethernet_hdr *)packet; + // const struct ethernet_hdr * ethernet = (struct ethernet_hdr *)packet; const struct ether_header * ethernet = (struct ether_header *)packet; /* this is the opposite endianness from http://www.iana.org/assignments/ethernet-numbers * TODO probably have to look at network/host byte order and endianness!! */ if (ethernet->ether_type == 0x0008) { - //const struct ip_hdr * ip = (struct ip_hdr *)(packet + sizeof(ether_header)); const struct ip * ip = (struct ip *)(packet + sizeof(ether_header)); if (ip->ip_p != 6) { @@ -119,7 +127,7 @@ Packet * getPacket (const struct pcap_pkthdr * header, const u_char * packet) #endif return NULL; } - const struct tcp_hdr * tcp = (struct tcp_hdr *)(packet + sizeof(ether_header) + sizeof(ip_hdr)); + const struct tcp_hdr * tcp = (struct tcp_hdr *)(packet + sizeof(ether_header) + sizeof(struct ip)); return new Packet (ip->ip_src, ntohs(tcp->th_sport), ip->ip_dst, ntohs(tcp->th_dport), header->len, header->ts); } else if (ethernet->ether_type == 0xDD86) { const struct ip6_hdr * ip6 = (struct ip6_hdr *)(packet + sizeof(ether_header)); @@ -134,8 +142,10 @@ Packet * getPacket (const struct pcap_pkthdr * header, const u_char * packet) const struct tcp_hdr * tcp = (struct tcp_hdr *)(packet + sizeof(ether_header) + sizeof(ip6_hdr)); // TODO make a Packet constructor that properly understands IPv6 - return new Packet (*((in_addr*)(&(ip6->ip6_src))), ntohs(tcp->th_sport), - *((in_addr*)(&(ip6->ip6_dst))), ntohs(tcp->th_dport), header->len, header->ts); + //return new Packet (*((in_addr*)(&(ip6->ip6_src))), ntohs(tcp->th_sport), + // *((in_addr*)(&(ip6->ip6_dst))), ntohs(tcp->th_dport), header->len, header->ts); + return new Packet (ip6->ip6_src, ntohs(tcp->th_sport), + ip6->ip6_dst, ntohs(tcp->th_dport), header->len, header->ts); } #if DEBUG @@ -149,12 +159,23 @@ Packet::Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned s sip = m_sip; sport = m_sport; dip = m_dip; dport = m_dport; len = m_len; time = m_time; - dir = m_dir; + dir = m_dir; sa_family = AF_INET; +} + +Packet::Packet (in6_addr m_sip, unsigned short m_sport, in6_addr m_dip, unsigned short m_dport, bpf_u_int32 m_len, timeval m_time, direction m_dir) +{ + sip6 = m_sip; sport = m_sport; + dip6 = m_dip; dport = m_dport; + len = m_len; time = m_time; + dir = m_dir; sa_family = AF_INET6; } Packet * Packet::newInverted () { /* TODO if this is a bottleneck, we can calculate the direction */ - return new Packet (dip, dport, sip, sport, len, time, dir_unknown); + if (sa_family == AF_INET) + return new Packet (dip, dport, sip, sport, len, time, dir_unknown); + else + return new Packet (dip6, dport, sip6, sport, len, time, dir_unknown); } /* constructs returns a new Packet() structure with the same contents as this one */ @@ -184,12 +205,33 @@ bool Packet::Outgoing () { case dir_incoming: return false; case dir_unknown: - if (local_addrs->contains(sip.s_addr)) { - dir = dir_outgoing; - return true; + bool islocal; + if (sa_family == AF_INET) + islocal = local_addrs->contains(sip.s_addr); + else + islocal = local_addrs->contains(sip6); + if (islocal) { + dir = dir_outgoing; + return true; } else { - dir = dir_incoming; - return false; + /*if (DEBUG) { + if (sa_family == AF_INET) + islocal = local_addrs->contains(dip.s_addr); + else + islocal = local_addrs->contains(dip6); + if (!islocal) { + std::cerr << "Neither dip nor sip are local: "; + char addy [50]; + inet_ntop (AF_INET6, &sip6, addy, 49); + std::cerr << addy << std::endl; + inet_ntop (AF_INET6, &dip6, addy, 49); + std::cerr << addy << std::endl; + + return false; + } + }*/ + dir = dir_incoming; + return false; } } } @@ -198,19 +240,29 @@ bool Packet::Outgoing () { /* '1.2.3.4' should be the local address. */ char * Packet::gethashstring () { - // TODO this needs to be bigger to support ipv6?! - char * tempretval = (char *) malloc (92 * sizeof(char)); - char * retval = (char *) malloc (92 * sizeof(char)); - if (Outgoing()) { - snprintf(tempretval, 92 * sizeof(char), "%s:%d-", inet_ntoa(sip), sport); - snprintf(retval, 92 * sizeof(char), "%s%s:%d", tempretval, inet_ntoa(dip), dport); + char * retval = (char *) malloc (HASHKEYSIZE * sizeof(char)); + char * local_string = (char*) malloc (50); + char * remote_string = (char*) malloc (50); + if (sa_family == AF_INET) { + inet_ntop(sa_family, &sip, local_string, 49); + inet_ntop(sa_family, &dip, remote_string, 49); + if (DEBUG) + fprintf(stderr, "Generating IPv4 string: "); } else { - snprintf(tempretval, 92 * sizeof(char), "%s:%d-", inet_ntoa(dip), dport); - snprintf(retval, 92 * sizeof(char), "%s%s:%d", tempretval, inet_ntoa(sip), sport); + inet_ntop(sa_family, &sip6, local_string, 49); + inet_ntop(sa_family, &dip6, remote_string, 49); + if (DEBUG) + fprintf(stderr, "Generating IPv6 string: "); } - //if (DEBUG) - //cout << "hasshtring: " << retval << endl; - free (tempretval); + if (Outgoing()) { + snprintf(retval, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string, sport, remote_string, dport); + } else { + snprintf(retval, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", remote_string, dport, local_string, sport); + } + free (local_string); + free (remote_string); + if (DEBUG) + std::cout << retval << std::endl; return retval; } diff --git a/packet.h b/packet.h index 375b323..c70bdc6 100644 --- a/packet.h +++ b/packet.h @@ -26,6 +26,8 @@ void getLocal (const char *device); class Packet { public: + in6_addr sip6; + in6_addr dip6; in_addr sip; in_addr dip; unsigned short sport; @@ -34,6 +36,7 @@ public: timeval time; Packet (in_addr m_sip, unsigned short m_sport, in_addr m_dip, unsigned short m_dport, bpf_u_int32 m_len, timeval m_time, direction dir = dir_unknown); + Packet (in6_addr m_sip, unsigned short m_sport, in6_addr m_dip, unsigned short m_dport, bpf_u_int32 m_len, timeval m_time, direction dir = dir_unknown); /* using default copy constructor */ /* Packet (const Packet &old_packet); */ /* copy constructor that turns the packet around */ @@ -48,6 +51,7 @@ public: char * gethashstring(); private: direction dir; + short int sa_family; }; Packet * getPacket (const struct pcap_pkthdr * header, const u_char * packet); diff --git a/process.cpp b/process.cpp index 53149a3..6e85985 100644 --- a/process.cpp +++ b/process.cpp @@ -8,7 +8,7 @@ #include #include #include - + #include "process.h" #include "hashtbl.h" #include "nethogs.h" @@ -75,6 +75,10 @@ HashTable * conninode = new HashTable (256); // TODO check what happens to the 'content' field of the hash 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 sockaddr_in6 localaddr, remaddr; @@ -84,6 +88,8 @@ void addtoconninode (char * buffer) extern struct aftype inet6_aftype; // the following line leaks memory. unsigned long * inode = (unsigned long *) malloc (sizeof(unsigned long)); + + // TODO check it matched sscanf(buffer, "%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X %*lX:%*lX %*X:%*lX %*lX %*d %*d %ld %*512s\n", local_addr, &local_port, rem_addr, &rem_port, inode); @@ -108,10 +114,9 @@ void addtoconninode (char * buffer) /* IPv4-compatible address */ if (DEBUG) fprintf (stderr, "IPv4-compatible address\n"); - ((struct sockaddr_in *)&localaddr)->sin_addr.s_addr = in6_local.s6_addr32[3]; - ((struct sockaddr_in *)&remaddr)->sin_addr.s_addr = in6_remote.s6_addr32[3]; - ((struct sockaddr *) &localaddr)->sa_family = AF_INET; - ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + result_addr_local = *((struct in6_addr*) &(in6_local.s6_addr32[3])); + result_addr_remote = *((struct in6_addr*) &(in6_remote.s6_addr32[3])); + sa_family = AF_INET; } else { /* real IPv6 address */ if (DEBUG) @@ -122,25 +127,28 @@ void addtoconninode (char * buffer) 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", &((struct sockaddr_in *)&localaddr)->sin_addr.s_addr); - sscanf(rem_addr, "%X", &((struct sockaddr_in *)&remaddr)->sin_addr.s_addr); - ((struct sockaddr *) &localaddr)->sa_family = AF_INET; - ((struct sockaddr *) &remaddr)->sa_family = AF_INET; + sscanf(local_addr, "%X", &result_addr_local); + sscanf(rem_addr, "%X", &result_addr_remote); + sa_family = AF_INET; } - /* Construct hash key and add inode to conninode table */ - char * temphashkey = (char *) malloc (92 * sizeof(char)); - char * hashkey = (char *) malloc (92 * sizeof(char)); + char * hashkey = (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); - /* TODO make this support the ipv6 addresses properly */ - snprintf(temphashkey, 92 * sizeof(char), "%s:%d-", inet_ntoa(((struct sockaddr_in *)&localaddr)->sin_addr), local_port); - snprintf(hashkey, 92 * sizeof(char), "%s%s:%d", temphashkey, inet_ntoa(((struct sockaddr_in *)&remaddr)->sin_addr), rem_port); - free (temphashkey); + snprintf(hashkey, HASHKEYSIZE * sizeof(char), "%s:%d-%s:%d", local_string, local_port, remote_string, rem_port); + free (local_string); + free (remote_string); if (DEBUG) fprintf (stderr, "Hashkey: %s\n", hashkey); @@ -449,11 +457,26 @@ Process * getProcess (Connection * connection, char * devicename) inode = (unsigned long *) conninode->get(connection->refpacket->gethashstring()); if (inode == NULL) { + /* HACK: the following is a hack for cases where the 'local' addresses + * aren't properly recognised, as is currently the case for IPv6 */ + + /* we reverse the direction of the stream if successful. */ + + Packet * reversepacket = connection->refpacket->newInverted(); + //inode = (unsigned long *) conninode->get(reversepacket->gethashstring()); + + if (inode == NULL) + { + delete reversepacket; #if DEBUG - std::cerr << connection->refpacket->gethashstring() << " STILL not in table - adding to the unknown process\n"; + std::cerr << connection->refpacket->gethashstring() << " STILL not in table - adding to the unknown process\n"; #endif - unknownproc->connections = new ConnList (connection, unknownproc->connections); - return unknownproc; + unknownproc->connections = new ConnList (connection, unknownproc->connections); + return unknownproc; + } + + delete connection->refpacket; + connection->refpacket = reversepacket; } }