/*- * Copyright (c) 2001-2005 Christian S.J. Peron * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "@(#) $Header: /usr/cvs/ipex/ipex.c,v 1.51 2005/05/18 17:32:38 modulus Exp $"; #endif #include #define READ_TIMEOUT 1000 #define NO_PRINT 0x7f static char **protocols; static char **tcp_ports; static char **udp_ports; /* * lookup table, the function pointer in delegate will initialize by * retrieving the * ip_p section of the IP header and using it to * cross reference it's arbitrary handler in this table. */ static struct proto_printers { u_char pp_proto; /* ip protocol */ int pp_hlen; /* required header length */ int (*pp_printer)(u_char *, struct pcap_pkthdr *, u_char *); } p_funcs[] = { { IPPROTO_TCP, 24, print_tcp }, { IPPROTO_ICMP, 4, print_icmp }, { IPPROTO_UDP, 8, print_udp }, { NO_PRINT, 0, generic_print }, { 0, 0, NULL } }; pcap_t *pd = NULL; pcap_dumper_t *pdmper; Heap *conqueue; static int offline(void) { char ebuf[32]; pd = pcap_open_offline(opts.rflag, ebuf); if (pd == NULL) errx(EX_DATAERR, "fatal: pcap_open_offline: failed to " "init pcap: %s", ebuf); } int main(int argc, char *argv []) { char ebuf[PCAP_ERRBUF_SIZE + 32]; int fg = 1, ch, cnt, snaplen = 65535; glob_t g; FILE *cmdfp; char *cmdbuf, *fname, *rem; u_char *pargs; static struct in_addr localnet, netmask; struct bpf_program bpfcode; struct pcap_stat pstat; struct dump_info info; pcap_handler printer; struct stat sb; cmdbuf = NULL; memset(&opts, 0, sizeof(opts)); opts.thiszone = gmt2local(0); opts.qflag = 1; while ((ch = getopt(argc, argv, "a:c:df:e:hi:lnpqr:t:u:vw:xB:C:DE:F:HLNOP:\ RST:X")) != -1) switch(ch) { case 'a': if (strncasecmp(optarg, "all", 4) == 0) { g.gl_offs=1; glob("*.dmp*", 0, NULL, &g); for(cnt = 0; cnt < g.gl_pathc; cnt++) { fname = chomp(g.gl_pathv[cnt]); disassemble_fname(fname); } } else { fname = chomp(optarg); disassemble_fname(fname); } exit(EX_OK); break; /* NOT REACHED */ case 'c': opts.cflag = strtoul(optarg, &rem, 0); if (*rem || rem == optarg || opts.cflag > LONG_MAX || !opts.cflag) errx(EX_USAGE, "invalid packet count" \ " specification."); break; case 'd': opts.dflag++; break; case 'e': opts.eflag = optarg; readtimespec(opts.eflag); break; case 'f': opts.fflag = optarg; if (stat(opts.fflag, &sb) < 0) err(1, "stat failed"); cmdbuf = malloc(sb.st_size); if (cmdbuf == NULL) err(1, "malloc failed"); if ((cmdfp = fopen(opts.fflag, "r")) == NULL) errx(EX_NOINPUT, "unable to open rules file."); logic_load(cmdbuf, cmdfp); fclose(cmdfp); break; case 'h': usage(NULL); exit(EX_USAGE); break; /* NOT REACHED */ case 'i': opts.iflag = optarg; break; case 'l': setvbuf(stdout, NULL, _IOLBF, 0); break; case 'n': opts.nflag++; break; case 'p': opts.pflag++; break; case 'q': opts.qflag = 0; break; case 'r': opts.rflag = optarg; break; case 't': opts.tflag = strtoul(optarg, &rem, 0); if (*rem || rem == optarg || opts.uflag > LONG_MAX || !opts.tflag) errx(EX_USAGE, "invalid timeout specification."); break; case 'u': opts.uflag = strtoul(optarg, &rem, 0); if (*rem || rem == optarg || opts.uflag > LONG_MAX || !opts.uflag) errx(EX_USAGE, "invalid UID specification."); break; case 'v': printf("%s\n", _VERSION); exit(0); break; case 'w': opts.wflag = optarg; break; case 'x': opts.xflag++; break; case 'B': opts.Bflag = optarg; ln_pattern(opts.Bflag); break; case 'C': opts.Cflag = parseCflag(optarg); if (!opts.Cflag) errx(1, "size specification out of bounds"); break; case 'D': opts.Dflag++; break; case 'E': opts.Eflag = optarg; break; case 'H': /* If we are just processing packet headers then * there is no point is capturing the entire frame * off the wire. Set the snaplen to 100. */ snaplen = 100; opts.Hflag++; break; case 'L': opts.Lflag++; break; case 'O': opts.Oflag = 1; break; case 'R': opts.Rflag++; break; case 'P': #if !defined __POWERPC__ && !defined linux opts.Pflag = optarg; scan_descriptor_tables(opts.Pflag); cmdbuf = (char *)compile_logic_list(); #else errx(EX_USAGE, "-P not yet supported on this system."); #endif break; case 'T': opts.Tflag = atoi(optarg); break; default: usage(NULL); exit(EX_USAGE); } argv += optind; argc -= optind; if (!opts.iflag && !opts.rflag) if (!(opts.iflag = pcap_lookupdev(ebuf))) errx(EX_USAGE, "fatal: pcap_lookupdev: %s", ebuf); handle_signals(); if (!opts.pflag) init_tabs(); if (!opts.fflag && !opts.Pflag) { cmdbuf = retrive_pcap_expr(argv); if (cmdbuf == NULL) cmdbuf = " "; } if (opts.rflag != NULL) offline(); else { pd = pcap_open_live(opts.iflag, snaplen, opts.qflag, READ_TIMEOUT, ebuf); if (pd == NULL) errx(EX_DATAERR, "fatal: pcap_open_live: %s", ebuf); } #if (!defined(linux)) if (opts.Rflag && !opts.wflag) if (ioctl(pd->fd, BIOCIMMEDIATE, &fg) < 0) errx(EX_OSERR, "fatal: ioctl(BIOCIMMEDIATE) failed."); #endif if (opts.wflag) { pdmper = pcap_dump_open(pd, opts.wflag); if (pdmper == NULL) errx(EX_NOINPUT, "fatal: pcap_dump_open: %s", pcap_geterr(pd)); info.WFileName = opts.wflag; info.pd = pd; info.p = pdmper; } fprintf(stderr, "ipex: reading packets from %s\n", opts.rflag ? opts.rflag : opts.iflag); if (!opts.rflag) { if (pcap_lookupnet(opts.iflag, &localnet.s_addr, &netmask.s_addr, ebuf) < 0) { memset(&localnet, 0, sizeof(localnet)); memset(&netmask, 0, sizeof(netmask)); fprintf(stderr,"warning: pcap_lookupnet failed\n"); } } if (pcap_compile(pd, &bpfcode, cmdbuf, opts.Oflag, netmask.s_addr) < 0) errx(EX_DATAERR, "fatal: pcap_compile: %s", pcap_geterr(pd)); if (pcap_setfilter(pd, &bpfcode) < 0) errx(EX_DATAERR, "fatal: pcap_setfilter() failed"); if ((opts.loffset = datalink_lookup_offset(pd)) == -1) errx(EX_UNAVAILABLE, "fatal: unsupported interface type"); printer = (pcap_handler)delegate; pargs = (caddr_t)&info; pcap_loop(pd, opts.cflag, printer, pargs); if (!opts.rflag) { pcap_stats(pd, &pstat); fprintf(stderr, "\nsession statistics: %d packets" "received, %d dropped\n", pstat.ps_recv, pstat.ps_drop); } pcap_close(pd); return (0); } void cleanup(void) { struct pcap_stat pstat; pcap_stats(pd, &pstat); fprintf(stderr, "\ninterrupted: session stats: %d packets received, " "%d dropped\n", pstat.ps_recv, pstat.ps_drop); free(protocols); free(tcp_ports); free(udp_ports); flush_ns_cache(); if (opts.Tflag) heap_destory(conqueue); pcap_close(pd); exit(0); } struct proto_printers * get_proto_printer(u_char proto, int caplen) { struct proto_printers *p; for (p = p_funcs; p->pp_printer; p++) if (proto == p->pp_proto) { /* * extra check here to make sure we have enough * data from the wire to align the proper protocol * header later. */ if (caplen >= p->pp_hlen) return (p); else break; } return (NULL); } int datalink_lookup_offset(pcap_t *p) { switch(pcap_datalink(p)) { case DLT_EN10MB: case DLT_IEEE802: return(ETHHDR_SIZE); case DLT_FDDI: return(FDDIHDR_SIZE); case DLT_SLIP: return(SLIPHDR_SIZE); case DLT_PPP: return(PPPHDR_SIZE); case DLT_RAW: return(RAWHDR_SIZE); #ifdef HAVE_NET_IF_PFLOG_H case DLT_PFLOG: return(PFLOG_HDRLEN); #endif case DLT_NULL: return(LOOPHDR_SIZE); default: break; } return(-1); /* unknown datalink layer type */ } pcap_handler delegate(u_char *pargs, struct pcap_pkthdr *h, u_char *p) { struct ip *ip; struct proto_printers *pp, pr; int match; char block[65535]; struct offset off; if (opts.Eflag) { memset(&off, 0, sizeof(off)); memcpy(&block[0], p, h->len); rettext(&block[0], h->len); block[h->len] = 0; if (!grep(opts.Eflag, &block[0], &off)) return (NULL); } if (opts.eflag) { match = ((h->ts.tv_sec >= dsp->lower) && (h->ts.tv_sec <= dsp->upper)); if (!match) return (NULL); } if (opts.Bflag != NULL) if (!detectpattern(p, h->len)) return (NULL); if (opts.Tflag) { handle_sessions(h, p); /* XXX should we be printing the sessions? */ return (NULL); } if (opts.tflag) { statetrack(pargs, h, p); return (NULL); } if (opts.wflag) { dump_and_trunc(pargs, h, p); return (NULL); } ip = (struct ip *)(p + opts.loffset); pp = get_proto_printer(ip->ip_p, h->caplen); if (pp == NULL) { pp = ≺ pp->pp_printer = generic_print; } pp->pp_printer(NULL, h, p); if (opts.Hflag) return (NULL); if (opts.xflag) ascii_print_with_offset(p, h->len, 0, 1); else text_dump(p, h->len, &off); return (NULL); } int usage(char *base) { fprintf(stderr, "usage: ipex [-hvnqbHxTLGdO] [-f config] [-r pattern] [-o outfile]\n" " [-c count] [-i interface] [-u uid] [-P op=arg]\n" " [-t time] [-w file] [-F file] [expression] [-B file]\n"); return (1); } /* Service lookup routines were snagged from IP Filter's ipmon. */ void init_tabs(void) { struct protoent *p; struct servent *s; char *name, **tab; int port; if (protocols != NULL) { free(protocols); protocols = NULL; } protocols = memalloc(256 * sizeof(*protocols)); memset(protocols, 0, 256 * sizeof(*protocols)); setprotoent(1); while ((p = getprotoent()) != NULL) if (p->p_proto >= 0 && p->p_proto <= 255 && p->p_name != NULL) protocols[p->p_proto] = strdup(p->p_name); endprotoent(); if (udp_ports != NULL) { free(udp_ports); udp_ports = NULL; } udp_ports = memalloc(65536 * sizeof(*udp_ports)); if (udp_ports != NULL) memset(udp_ports, 0, 65536 * sizeof(*udp_ports)); if (tcp_ports != NULL) { free(tcp_ports); tcp_ports = NULL; } tcp_ports = memalloc(65536 * sizeof(*tcp_ports)); if (tcp_ports != NULL) memset(tcp_ports, 0, 65536 * sizeof(*tcp_ports)); setservent(1); while ((s = getservent()) != NULL) { if (s->s_proto == NULL) continue; else if (!strcmp(s->s_proto, "tcp")) { port = ntohs(s->s_port); name = s->s_name; tab = tcp_ports; } else if (!strcmp(s->s_proto, "udp")) { port = ntohs(s->s_port); name = s->s_name; tab = udp_ports; } else continue; if ((port < 0 || port > 65535) || (name == NULL)) continue; tab[port] = strdup(name); } return; } char * getproto(u_int p) { static char pnum[4]; char *s; p &= 0xff; s = protocols ? protocols[p] : NULL; if (s == NULL) { sprintf(pnum, "%u", p); s = pnum; } return (s); } char * portname(int res, char *proto, u_int port) { static char pname[8]; char *s; port = ntohs(port); port &= 0xffff; if (opts.pflag) { sprintf(pname, "%u", port); return (&pname[0]); } s = NULL; if (!strcmp(proto, "tcp")) s = tcp_ports[port]; else if (!strcmp(proto, "udp")) s = udp_ports[port]; if (s == NULL) { sprintf(pname, "%u", port); s = pname; } return (s); }