#include #include #include extern "C" { #include } #include #include #include #include #include #include "tlmptcpdump.h" #include "tlmptcpdump.m" #include using namespace std; static bool debug=false; class _F_tcpdump_private{ public: bool end; _F_tcpdump_private() { end = false; } }; static void n_debug(const char *ctl, ...) { if (debug){ va_list list; va_start (list,ctl); vfprintf (stderr,ctl,list); va_end (list); } } PUBLIC bool TCPSESSION::operator < (const TCPSESSION &c) const { bool ret = false; if (saddr < c.saddr){ ret = true; }else if (saddr == c.saddr){ if (daddr < c.daddr){ ret = true; }else if (daddr == c.daddr){ if (sport < c.sport){ ret = true; }else if (sport == c.sport){ if (dport < c.dport){ ret = true; } } } } return ret; } PUBLIC TCPDUMP_STATS::TCPDUMP_STATS() { to.nbpk = 0; to.size = 0; to.stamp.tv_usec = to.stamp.tv_sec = 0; to.seq = 0; from.nbpk = 0; from.size = 0; from.stamp.tv_usec = from.stamp.tv_sec = 0; from.seq = 0; finfrom = finto = finack = false; synfrom = synto = synack = false; } struct TCPSESSION_STATS: public TCPDUMP_STATS{ ARRAY_OBJ *data; bool discard; TCPSESSION_STATS(){ data = NULL; discard = false; } ~TCPSESSION_STATS(){ // fprintf (stderr,"delete data %p\n",data); delete data; } }; typedef map TCPTB; static TCPTB tcptb; typedef TCPTB::iterator TCPTBI; int _F_tcpdump::getnbsession() { return tcptb.size(); } void _F_tcpdump::idle(bool &) { } void _F_tcpdump::missing( int no, const TCPSESSION &session, bool from, ARRAY_OBJ *, bool &, bool &, unsigned long expect, unsigned long got) { fprintf (stderr,"Out of order packet for TCP session %08x:%u %s %08x:%u (%lu != %lu)\n" ,session.saddr,session.sport ,from ? "->" : "<-" ,session.daddr,session.dport ,expect,got); } static bool ifany = false; static void fct(u_char *mydata, const pcap_pkthdr *pk, const u_char *pkdata) { struct _F_tcpdump *c = (_F_tcpdump*)mydata; if (c->priv->end) return; int dev = 0; if (ifany){ dev = ((unsigned)pkdata[0] << 8) + pkdata[1]; pkdata+=2; } struct ether_header *eh = (struct ether_header*)pkdata; int ether_type = ntohs (eh->ether_type); if (debug){ printf ("caplen=%-5d dev=%-5d ",pk->caplen,dev); for (int i=0; i<6; i++) printf (i==0 ? "%02x" : ":%02x",pkdata[i]); printf (" "); for (int i=6; i<12; i++) printf (i==6 ? "%02x" : ":%02x",pkdata[i]); printf (" %04x",ether_type); printf (" - "); for (int i=14; i<34; i++) printf (i==14 ? "%02x" : ":%02x",pkdata[i]); printf (" | "); for (int i=34; i<40; i++) printf (i==34 ? "%02x" : ":%02x",pkdata[i]); printf ("\n"); } if (ether_type == ETHERTYPE_IP){ struct iphdr *hdr = (struct iphdr*)(pkdata+14); if (hdr->version == IPVERSION){ // We have to skip the options to locate the tcp header const u_char *opt = (u_char*)(hdr + 1); unsigned tot_len = ntohs(hdr->tot_len); unsigned hdr_len = hdr->ihl << 2; while ((opt-(u_char*)hdr) < hdr_len){ n_debug ("opt = %u len=%u\n",*opt,opt[1]); unsigned optval = (*opt)&0x1f; if (optval == 0){ opt++; break; } switch (optval){ case 1: opt++; break; case 2: opt += opt[1]; break; case 3: opt += opt[1]; break; case 4: opt += opt[1]; break; case 7: opt += opt[1]; break; case 8: //opt += opt[1]; opt++; break; case 9: opt += opt[1]; break; default: printf ("Unknown IP option\n"); opt++; break; } } int off = opt-(u_char*)hdr; while (off % 4 != 0) off++; opt = (u_char*)hdr + off; if (hdr->protocol == IPPROTO_TCP){ struct tcphdr *tcp = (struct tcphdr*)opt; #if 0 printf ("\tversion=%u,ihl=%u,tos=%u,tot_len=%u,id=%u,frag_off=%u\n\tttl=%u,proto=%u,check=%u,saddr=%08x,daddr=%08x\n" ,hdr->version,hdr->ihl,hdr->tos, tot_len, hdr->id, ntohs(hdr->frag_off), hdr->ttl, hdr->protocol, hdr->check, hdr->saddr, hdr->daddr); printf ("\tsport=%u dport=%u\n",ntohs(tcp->source),ntohs(tcp->dest)); #endif u_char *data = (u_char*)tcp + (tcp->doff<<2); int datasize = tot_len - (data-pkdata) + 14; TCPSESSION tcps; tcps.saddr = ntohl(hdr->saddr); tcps.daddr = ntohl(hdr->daddr); tcps.sport = ntohs(tcp->source); tcps.dport = ntohs(tcp->dest); // Try to locate the socket using one direction TCPTBI p = tcptb.find(tcps); #if 0 if (tcp->fin) printf ("Fin session\n"); if (tcp->rst) printf ("Rst session\n"); if (tcp->ack) printf ("Ack session\n"); if (tcp->psh) printf ("Psh session\n"); if (tcp->urg) printf ("Urg session\n"); if (tcp->syn) printf ("Syn session\n"); #endif if (p != tcptb.end()){ // Ok the socket was seen before p->second.from.nbpk++; p->second.from.size += datasize; p->second.from.stamp.tv_sec = pk->ts.tv_sec; p->second.from.stamp.tv_usec = pk->ts.tv_usec; unsigned long seq = ntohl(tcp->seq); if (0 && seq != p->second.from.seq && !p->second.discard){ c->missing (0,tcps,true,p->second.data,c->priv->end ,p->second.discard,seq,p->second.from.seq); p->second.from.seq = seq; } if (tcp->fin){ p->second.finfrom = true; }else if (tcp->ack && p->second.finfrom && p->second.finto){ if (!p->second.discard){ c->endsession (0,tcps,p->second.data,c->priv->end,true); } tcptb.erase(tcps); }else{ n_debug ("Already seen from %d %d size=%d\n",p->second.from.nbpk ,p->second.to.nbpk,datasize); if (datasize > 0 && !p->second.discard){ c->packet (0,tcps,p->second.data,c->priv->end,true,p->second,data,datasize); p->second.from.seq += datasize; } } }else{ // Try the opposite TCPSESSION rtcps; rtcps.daddr = tcps.saddr; rtcps.saddr = tcps.daddr; rtcps.dport = tcps.sport; rtcps.sport = tcps.dport; p = tcptb.find(rtcps); if (p != tcptb.end()){ p->second.to.nbpk++; p->second.to.size += datasize; p->second.to.stamp.tv_sec = pk->ts.tv_sec; p->second.to.stamp.tv_usec = pk->ts.tv_usec; unsigned long seq = ntohl(tcp->seq); if (0 && seq != p->second.to.seq && !p->second.discard){ c->missing (0,rtcps,false,p->second.data,c->priv->end ,p->second.discard ,seq,p->second.to.seq); p->second.to.seq = seq; } if (tcp->fin){ p->second.finto = true; }else if (tcp->ack && p->second.finfrom && p->second.finto){ if (!p->second.discard){ c->endsession (0,rtcps,p->second.data,c->priv->end,false); } tcptb.erase(rtcps); }else{ n_debug ("Already seen to %d %d\n",p->second.from.nbpk ,p->second.to.nbpk); if (datasize > 0 && !p->second.discard){ c->packet (0,rtcps,p->second.data,c->priv->end,false,p->second,data,datasize); p->second.to.seq += datasize; } } }else{ n_debug ("New session\n"); bool discard = false, reverse=false; ARRAY_OBJ *udata = NULL; c->newsession(0,tcps,udata,c->priv->end,discard,reverse); TCPSESSION_STATS *s; if (reverse){ s = &tcptb[rtcps]; }else{ s = &tcptb[tcps]; } s->from.nbpk = 1; s->from.size = datasize; s->from.stamp.tv_sec = pk->ts.tv_sec; s->from.stamp.tv_usec = pk->ts.tv_usec; s->discard = discard; s->from.seq = ntohl(tcp->seq); if (discard){ delete udata; udata = NULL; } s->data = udata; if (datasize > 0 && !discard){ c->packet (0,tcps,s->data,c->priv->end,true,*s,data,datasize); s->from.seq += datasize; } } } }else if (hdr->protocol == IPPROTO_UDP){ n_debug ("\tUDP %08x:%08x\n",hdr->saddr,hdr->daddr); }else if (hdr->protocol == IPPROTO_ICMP){ n_debug ("\tICMP %08x:%08x\n",hdr->saddr,hdr->daddr); }else{ n_debug ("\tAutre proto IP %d\n",hdr->protocol); } }else{ n_debug ("\tPas IPV4\n"); } }else if (ether_type == ETHERTYPE_ARP){ n_debug ("\tArp\n"); }else{ n_debug ("\tPas IP\n"); } } void tcpdump ( _F_tcpdump &c, const char *dev, const char *filter, bool promisc) { ifany = false; if (dev == NULL || strcmp(dev,"any")==0) ifany = true; _F_tcpdump_private priv; c.priv = &priv; if (dev == NULL){ char errbuf[PCAP_ERRBUF_SIZE]; dev = pcap_lookupdev (errbuf); if (dev == NULL){ tlmp_error (MSG_U(E_NODEFDEV ,"tlmptcpdump: No default device to use\n%s\n") ,errbuf); } } if (dev != NULL){ char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *p = pcap_open_live ((char*)dev,60000,promisc,500,errbuf); if (p != NULL){ if (filter != NULL){ struct bpf_program bpf; pcap_compile (p,&bpf,(char*)filter,1,0xffffffff); pcap_setfilter (p,&bpf); } while (!priv.end){ pcap_dispatch (p,1000,fct,(u_char*)&c); } pcap_close (p); }else{ tlmp_error (MSG_U(E_NODEV,"Can't access device %s\n") ,dev); } } }