/* * pcapdispatcher --- dispatch packets by protocol, cnx, etc. * see http://www.secdev.org/projects/pcapdispatcher.html * for more informations * * Copyright (C) 2003 Philippe Biondi * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ /* $Id: pcapdispatcher.c,v 1.7 2003/10/25 12:52:53 pbi Exp pbi $ */ #define REVISION "$Revision: 1.7 $" #include #include #include #include #include #include #include #include #include #include #include #include #define MINPKTGRP 3 #define PERROR(x) do{ perror(x); exit(-1); }while(0); #define ERROR(x...) do{fprintf(stderr, "ERROR: " x);exit(-1);}while(0) #define DEBUGno #ifdef DEBUG #define DBG(x...) do{fprintf(stderr, "DBG: " x);}while(0) #else #define DBG(x...) #endif void usage(void) { char version[64] = REVISION; version[9]='0'; version[10]='.'; version[strlen(version)-2] = '\x00'; fprintf(stderr, "PCAPdispatcher v%s (C) 2003 Philippe Biondi \n",version+9); fprintf(stderr, "Usage: pcapdispatcher -i input [-o outputdir] [-f bpffilter]\n"); exit(0); } typedef struct cnx_s { int src,dst; short sport,dport; } cnx_t; typedef union hashval_u { int type; cnx_t cnx; } hashval_t; typedef struct node_s node_t; typedef hashval_t (*hashfunc_t)(struct pcap_pkthdr *phd, u_char *pkt, int *ofs); typedef void (*dispatch_cb_t)(node_t *n, pcap_t *cap, struct pcap_pkthdr *phd, u_char *pkt, int ofs); struct node_s { node_t *next, *child; dispatch_cb_t dispatch; hashfunc_t H_func; hashval_t hv; pcap_dumper_t *dumper; char *filename; int fifo_size; struct pcap_pkthdr fifo_phd[MINPKTGRP]; u_char *fifo_pkt[MINPKTGRP]; }; struct cbdata_s { pcap_t *cap; node_t *root; }; node_t *alloc_node(int id, char *filename, dispatch_cb_t dispatch, hashfunc_t H, node_t *child, node_t *next) { node_t *n; n = malloc(sizeof(node_t)); if (!n) PERROR("malloc"); bzero(n, sizeof(node_t)); n->hv.type = id; n->filename = strdup(filename); n->next = next; n->child = child; n->dispatch = dispatch; n->H_func = H; return n; } void free_nodes(node_t *n, pcap_t *cap, char *lastname, pcap_dumper_t **dumper) { do { if (n->dumper) pcap_dump_close(n->dumper); else { if (n->fifo_size > 0) { int i; DBG("Node not dumped :[%s]\n", lastname); if (!*dumper) { *dumper = pcap_dump_open(cap, lastname); if (!*dumper) ERROR("pcap_dump_open(%s): %s\n", n->filename, pcap_geterr(cap)); } for (i = 0; i < n->fifo_size; i++) pcap_dump((u_char *)*dumper, &n->fifo_phd[i], n->fifo_pkt[i]); } } if (n->child) { char buf[64]; pcap_dumper_t *p = NULL; snprintf(buf,64,"remain-%s",n->filename); free_nodes(n->child, cap, buf, &p); if (p) { DBG("Closing [%s]\n", buf); pcap_dump_close(p); } } n = n->next; } while(n); } /**********************************************************************************/ /* Hash functions */ /******************/ hashval_t H_ether(struct pcap_pkthdr *phd, u_char *pkt, int *ofs) { hashval_t H; struct ether_header *eh; eh = (struct ether_header*)(pkt+*ofs); if (phd->caplen >= *ofs+sizeof(struct ether_header)) { H.type = ntohs(eh->ether_type); *ofs += sizeof(struct ether_header); } else H.type = -1; return H; } hashval_t H_ip(struct pcap_pkthdr *phd, u_char *pkt, int *ofs) { hashval_t H; struct iphdr *ih; ih = (struct iphdr*)(pkt+*ofs); if (phd->caplen >= *ofs+sizeof(struct iphdr)) H.type = ih->protocol; else H.type = -1; return H; } hashval_t H_tcpip(struct pcap_pkthdr *phd, u_char *pkt, int *ofs) { hashval_t H; struct iphdr *ih; struct tcphdr *th; ih = (struct iphdr *)(pkt+*ofs); th = (struct tcphdr *)(pkt+(*ofs+ih->ihl*4)); if ((ih->saddr > ih->daddr) || ((ih->saddr == ih->daddr) && (th->source > th->dest))) { H.cnx.src = ih->daddr; H.cnx.dst = ih->saddr; H.cnx.sport = ntohs(th->dest); H.cnx.dport = ntohs(th->source); } else { H.cnx.src = ih->saddr; H.cnx.dst = ih->daddr; H.cnx.sport = ntohs(th->source); H.cnx.dport = ntohs(th->dest); } ofs += (ih->ihl+th->doff)*4; return H; } hashval_t H_udpip(struct pcap_pkthdr *phd, u_char *pkt, int *ofs) { hashval_t H; struct iphdr *ih; struct udphdr *uh; ih = (struct iphdr *)(pkt+*ofs); uh = (struct udphdr *)(pkt+*ofs+ih->ihl*4); if ((ih->saddr > ih->daddr) || ((ih->saddr == ih->daddr) && (uh->source > uh->dest))) { H.cnx.src = ih->daddr; H.cnx.dst = ih->saddr; H.cnx.sport = ntohs(uh->dest); H.cnx.dport = ntohs(uh->source); } else { H.cnx.src = ih->saddr; H.cnx.dst = ih->daddr; H.cnx.sport = ntohs(uh->source); H.cnx.dport = ntohs(uh->dest); } ofs += ih->ihl*4+sizeof(struct udphdr); return H; } /********************************************************************************/ /* CNX tools */ /*************/ char *cnx2name(cnx_t c) { char buf[64]; char buf2[64]; struct in_addr ins, ind; ins.s_addr = c.src; ind.s_addr = c.dst; strncpy(buf2,inet_ntoa(ins),63); snprintf(buf, 64, "tcp-%s:%hu-%s:%hu.cap", buf2, c.sport, inet_ntoa(ind), c.dport); /* snprintf(buf, 64, "tcp-%s-%s.cap", buf2, inet_ntoa(ind) );*/ return strdup(buf); } int cnx_equals(cnx_t c1, cnx_t c2) { /* return ((c1.src == c2.src && c1.sport == c2.sport && c1.dst == c2.dst && c1.dport == c2.dport) || (c1.src == c2.dst && c1.sport == c2.dport && c1.dst == c2.src && c1.dport == c2.sport)); */ return ((c1.src == c2.src && c1.dst == c2.dst) || (c1.src == c2.dst && c1.dst == c2.src)); } int cnx_cmp(cnx_t c1, cnx_t c2) { if (c1.src < c2.src) return -1; if (c1.src > c2.src) return 1; if (c1.dst < c2.dst) return -1; if (c1.dst > c2.dst) return 1; if (c1.sport < c2.sport) return -1; if (c1.sport > c2.sport) return 1; if (c1.dport < c2.dport) return -1; if (c1.dport > c2.dport) return 1; return 0; } /********************************************************************************/ /* Dispatch functions */ /**********************/ void final_dispatch(node_t *n, pcap_t *cap, struct pcap_pkthdr *phd, u_char *pkt, int ofs) { if (!n->dumper) { int i; if (n->fifo_size < MINPKTGRP) { i = n->fifo_size; n->fifo_phd[i] = *phd; n->fifo_pkt[i] = malloc(phd->caplen); if (!n->fifo_pkt[i]) PERROR("malloc"); memcpy(n->fifo_pkt[i], pkt, phd->caplen); n->fifo_size++; DBG("stored pkt #%i\n", n->fifo_size); return; } else { n->dumper = pcap_dump_open(cap, n->filename); if (!n->dumper) ERROR("pcap_dump_open(%s): %s\n", n->filename, pcap_geterr(cap)); for (i = 0; i < MINPKTGRP; i++) { pcap_dump((u_char *)n->dumper, &n->fifo_phd[i], n->fifo_pkt[i]); free(n->fifo_pkt[i]); } n->fifo_size = 0; /* should not be needed */ DBG("### flushes stored packets\n"); } } pcap_dump((u_char *)n->dumper, phd, pkt); } void normal_dispatch(node_t *n, pcap_t *cap, struct pcap_pkthdr *phd, u_char *pkt, int ofs) { node_t *child = n->child; hashval_t hv; hv = (*n->H_func)(phd, pkt, &ofs); if (hv.type != -1) { while (child) { if (child->hv.type == hv.type) { (*child->dispatch)(child, cap, phd, pkt, ofs); return; } child = child->next; } } final_dispatch(n, cap, phd, pkt, ofs); } void creation_dispatch(node_t *n, pcap_t *cap, struct pcap_pkthdr *phd, u_char *pkt, int ofs) { node_t **oc, *child = n->child; hashval_t hv; char buf[64]; oc = &n; hv = (*n->H_func)(phd, pkt, &ofs); if (hv.type != -1) { int cmp; node_t *nxt; oc = &(n->child); while (child) { cmp = -cnx_cmp(child->hv.cnx,hv.cnx); if (!cmp) { (*child->dispatch)(child, cap, phd, pkt, ofs); return; } else if (cmp > 0) { DBG("!!!!!!!!!!!!!!!!!!! SHORTCUT\n"); break; } oc = &child->next; child = child->next; } *oc = malloc(sizeof(node_t)); if (!*oc) PERROR("malloc"); bzero(*oc, sizeof(node_t)); (*oc)->filename = cnx2name(hv.cnx); (*oc)->dispatch = &final_dispatch; (*oc)->hv = hv; (*oc)->next = child; } final_dispatch(*oc, cap, phd, pkt, ofs); } void pcap_cb(u_char *user, struct pcap_pkthdr *phd, u_char *pkt) { struct cbdata_s *data = (struct cbdata_s *)user; node_t *root = data->root; DBG("--\n"); (*root->dispatch)(root, data->cap, phd, pkt, 0); } int main(int argc, char *argv[]) { char c; char *input=NULL, *outputdir="./", *filter=NULL; char errbuf[PCAP_ERRBUF_SIZE]; pcap_t *cap; struct bpf_program bpf; pcap_dumper_t *dumper; node_t *ether_root; struct cbdata_s cbdata; while ((c = getopt(argc, argv, "hi:o:f:")) != -1) { switch(c) { case 'h': usage(); case 'i': input = optarg; break; case 'o': outputdir = optarg; break; case 'f': filter = optarg; break; default: usage(); } } if (!input) usage(); cap = pcap_open_offline(input, errbuf); if (! cap) ERROR("pcap_open_offline: %s\n",errbuf); if (pcap_datalink(cap) != DLT_EN10MB) ERROR("Sorry, this programm only supports captures on Ethernet devices...\n"); if (chdir(outputdir) == -1) { if (errno == ENOENT) { DBG("Creating [%s]\n", outputdir); if ( mkdir(outputdir,0x1ff) == -1) PERROR("mkdir"); if (chdir(outputdir) == -1) PERROR("chdir"); } } if (filter) { if (pcap_compile(cap, &bpf, filter, 1, 0xffffff00) == -1) ERROR("pcap_compile: %s\n",pcap_geterr(cap)); if (pcap_setfilter(cap, &bpf) == -1) ERROR("pcap_setfilter: %s\n", pcap_geterr(cap)); } ether_root = alloc_node(0,"ether-other.cap", &normal_dispatch, &H_ether, alloc_node(0x800, "ip-other.cap", &normal_dispatch, &H_ip, alloc_node(1, "icmp.cap", &final_dispatch, NULL, NULL, alloc_node(6, "tcp.cap", &creation_dispatch, &H_tcpip, NULL, alloc_node(17,"udp.cap", &creation_dispatch, &H_udpip, NULL,NULL))), alloc_node(0x806, "arp.cap", &final_dispatch, NULL, NULL, NULL)), NULL); cbdata.root = ether_root; cbdata.cap = cap; if (pcap_dispatch(cap, -1, (void *)pcap_cb, (u_char *)&cbdata) == -1) ERROR("pcap_dispatch: %s\n", pcap_geterr(cap)); /* pcap_freecode(&bpf);*/ { pcap_dumper_t *p = NULL; free_nodes(ether_root,cap, "remain", &p); } pcap_close(cap); }