Files
nethogs/packet.cpp
Arnout Engelen e50d81eb5c preliminary IPv6 support.
besides ::ffff:123.45.67.89, IPv6 packets now also really enter the system.

characterisation of local addresses and direction of the stream don't work
yet, so they are often filed under 'unknown' instead of their respective
process.
2004-09-01 11:21:00 +00:00

224 lines
10 KiB
C++

#include "nethogs.h"
#include <iostream>
#include "packet.h"
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <malloc.h>
#include <assert.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <sys/ioctl.h>
// #include "inet6.c"
local_addr * local_addrs = NULL;
/*
* getLocal
* device: This should be device explicit (e.g. eth0:1)
*
* uses ioctl to get address of this device, and adds it to the
* local_addrs-list.
*/
void getLocal (const char *device)
{
int sock;
struct ifreq iFreq;
struct sockaddr_in *saddr;
if((sock=socket(AF_INET, SOCK_PACKET, htons(0x0806)))<0){
forceExit("creating socket failed while establishing local IP - are you root?");
}
strcpy(iFreq.ifr_name, device);
if(ioctl(sock, SIOCGIFADDR, &iFreq)<0){
forceExit("ioctl failed while establishing local IP");
}
saddr=(struct sockaddr_in*)&iFreq.ifr_addr;
local_addrs = new local_addr (saddr->sin_addr.s_addr, local_addrs);
}
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 {
u_short th_sport; /* source port */
u_short th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
#if BYTE_ORDER == LITTLE_ENDIAN
u_int th_x2:4, /* (unused) */
th_off:4; /* data offset */
#endif
#if BYTE_ORDER == BIG_ENDIAN
u_int th_off:4, /* data offset */
th_x2:4; /* (unused) */
#endif
u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
u_short th_win; /* window */
u_short th_sum; /* checksum */
u_short th_urp; /* urgent pointer */
};
/* 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 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)
{
#if DEBUG
std::cerr << "Dropped non-tcp IPv4 packet of type " << (int)(ip->ip_p) << std::endl;
#endif
return NULL;
}
const struct tcp_hdr * tcp = (struct tcp_hdr *)(packet + sizeof(ether_header) + sizeof(ip_hdr));
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));
if ((ip6->ip6_ctlun).ip6_un1.ip6_un1_nxt != 0x06)
{
// TODO maybe we need to skip over some headers?
#if DEBUG
std::cerr << "Dropped non-tcp IPv6 header of type " << (int)((ip6->ip6_ctlun).ip6_un1.ip6_un1_nxt) << std::endl;
#endif
return NULL;
}
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);
}
#if DEBUG
std::cerr << "Dropped non-ip packet of type " << ethernet->ether_type << std::endl;
#endif
return NULL;
}
Packet::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 m_dir)
{
sip = m_sip; sport = m_sport;
dip = m_dip; dport = m_dport;
len = m_len; time = m_time;
dir = m_dir;
}
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);
}
/* constructs returns a new Packet() structure with the same contents as this one */
/*Packet::Packet (const Packet &old_packet) {
sip = old_packet.sip; sport = old_packet.sport;
dip = old_packet.dip; dport = old_packet.dport;
len = old_packet.len; time = old_packet.time;
}*/
bool sameinaddr(in_addr one, in_addr other)
{
return one.s_addr == other.s_addr;
}
bool Packet::isOlderThan (timeval t) {
return (time.tv_sec + PERIOD <= t.tv_sec);
}
bool Packet::Outgoing () {
/* must be initialised with getLocal("eth0:1");) */
if (DEBUG)
assert (local_addrs != NULL);
switch (dir) {
case dir_outgoing:
return true;
case dir_incoming:
return false;
case dir_unknown:
if (local_addrs->contains(sip.s_addr)) {
dir = dir_outgoing;
return true;
} else {
dir = dir_incoming;
return false;
}
}
}
/* returns the packet in '1.2.3.4:5-1.2.3.4:5'-form, for use in the 'conninode' table */
/* '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);
} 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);
}
//if (DEBUG)
//cout << "hasshtring: " << retval << endl;
free (tempretval);
return retval;
}
/* 2 packets match if they have the same
* source and destination ports and IP's. */
bool Packet::match (Packet * other)
{
return (sport == other->sport) && (dport == other->dport)
&& (sameinaddr(sip, other->sip)) && (sameinaddr(dip, other->dip));
}