/* extract - A network log processor Copyright (C) 1993 Douglas Lee Schales, David K. Hess, David R. Safford Please see the file `COPYING' for the complete copyright notice. extract.c - 03/20/93 */ #include #include #include #include #include #include #include #include #include #include #include #include #include "extract.h" #include "chario.h" #include "parser.h" #include "interp.h" #include "stdunix.h" #include "version.h" extern char *inet_ntoa(struct in_addr); extern char *optarg; extern int optind; extern struct parsetree *parse(void); extern u_int64 extract(struct ftio *, struct parsetree *, struct ftver *, struct ftio *, int); extern char *getportname(unsigned short); extern void writebin(char *, struct ftio *); extern void writeascii(char *, struct ftver *, int); extern void writebuf(char *, struct ftver *, int, struct ftio *); extern void setlogmode(int); extern void setlogfile(FILE *); extern char *gettimestr(struct fttime); extern char *geticmpportname(unsigned short); static void helpscreen(char *); #define LOGBINARY 0 #define LOGASCII 1 int resolve = 1; int srvresolve = 1; #define TCPDATA 6 #define UDPDATA 17 #define ICMPDATA 1 int protoflag = TCPDATA; int oldicmpstyle = 0; int outlogmode = LOGBINARY; FILE *logfile; int logmode = LOGASCII; int main(int argc, char **argv) { struct parsetree *parsetree; FILE *outfile = stdout; FILE *dumpfile = stdin; int file = 0, expr = 0, errflag = 0, z_level = 6; int status; int oldflow; unsigned int streamtimes[2]; char *membufname; u_int64 total_flows; int c, out_byte_order; int out_fd_plain = 0; struct stat stat_buf; struct ftset ftset; struct ftver ftv, ftv2; struct ftio ftio_out, ftio_in; logfile = stdout; ftset_init(&ftset, 6); /* default compress 6 */ while((c=getopt(argc, argv, "vC:Dd:e:f:E:F:o:OabB:nhz:")) != -1){ switch(c){ case 'v': printf("flow-extract, version %s\n", toolversion); exit(0); break; case 'C': /* comment field */ ftset.comments = optarg; break; case 'B': if (!strcasecmp(optarg, "little")) ftset.byte_order = FT_HEADER_LITTLE_ENDIAN; else if (!strcasecmp(optarg, "big")) ftset.byte_order = FT_HEADER_BIG_ENDIAN; else fterr_errx(1, "expecting \"big\" or \"little\""); break; case 'a': logmode = LOGASCII; break; case 'b': logmode = LOGBINARY; case 'n': resolve = 0; srvresolve = 0; break; case 'D': srvresolve = 0; break; case 'f': if((status = addfile(optarg)) != NOERR){ errflag++; if(status == FILEERR){ perror(optarg); exit(1); } else { fprintf(stderr, "%s: unexpected error\n", optarg); exit(1); } } file++; break; case 'F': if((status = includefile(optarg)) != NOERR){ errflag++; if(status == FILEERR){ perror(optarg); exit(1); } else { fprintf(stderr, "%s: unexpected error\n", optarg); exit(1); } } file++; break; case 'e': membufname = (char *)malloc(20); sprintf(membufname, "argv[%d]", optind-1); if((status = addmembuf(optarg, membufname)) != NOERR){ fprintf(stderr, "-e: unexpected error\n"); errflag++; } expr++; break; case 'E': membufname = (char *)malloc(20); sprintf(membufname, "argv[%d]", optind-1); if((status = includemembuf(optarg, membufname)) != NOERR){ fprintf(stderr, "-E: unexpected error\n"); errflag++; } expr++; break; case 'd': if(!(dumpfile = fopen(optarg, "r"))){ perror(optarg); errflag++; } break; case 'o': if(!errflag && !(outfile = fopen(optarg, "w"))){ perror(optarg); errflag++; } break; case 'h': helpscreen(argv[0]); exit(0); break; case 'O': oldicmpstyle++; break; case 'z': ftset.z_level = atoi(optarg); if ((ftset.z_level < 0) || (ftset.z_level > 9)) { fprintf(stderr, "Compression level must be between 0 and 9\n"); exit (1); } break; default: errflag++; break; } } if(errflag) exit(1); if(!file && !expr) addmembuf("{print}", ""); if (fstat(fileno(outfile), &stat_buf) == -1) { fprintf(stderr, "stat(): %s\n", strerror(errno)); exit (1); } if (!stat_buf.st_rdev) out_fd_plain = 1; if (logmode == LOGBINARY) { if (ftio_init(&ftio_out, fileno(outfile), FT_IO_FLAG_WRITE | ((ftset.z_level) ? FT_IO_FLAG_ZINIT : 0) ) < 0) fterr_errx(1, "ftio_init(): failed"); bzero(&ftv, sizeof ftv); if (ftio_init(&ftio_in, fileno(dumpfile), FT_IO_FLAG_READ) < 0) fterr_errx(1, "ftio_init(): failed"); ftio_get_ver(&ftio_in, &ftv); streamtimes[0] = ftio_get_cap_start(&ftio_in); streamtimes[1] = ftio_get_cap_end(&ftio_in); /* the code doesn't handle outputting anything but the */ /* latest stream version. */ if (ftv.s_version < 3) oldflow = 1; else oldflow = 0; ftv.s_version = FT_IO_SVERSION; if (ftio_set_ver(&ftio_out, &ftv) < 0) fterr_errx(1, "ftio_set_ver(): failed"); ftio_set_comment(&ftio_out, ftset.comments); ftio_set_byte_order(&ftio_out, ftset.byte_order); ftio_set_z_level(&ftio_out, ftset.z_level); ftio_set_streaming(&ftio_out, 1); ftio_set_debug(&ftio_out, 0); /* init with sane values */ if (out_fd_plain) { ftio_set_cap_time(&ftio_out, streamtimes[0], streamtimes[1]); ftio_set_flows_count(&ftio_out, 0); } /* header first */ if (ftio_write_header(&ftio_out) < 0) fterr_errx(1, "ftio_write_header(): failed"); } else { if (ftio_init(&ftio_in, fileno(dumpfile), FT_IO_FLAG_READ) < 0) fterr_errx(1, "ftio_init(): failed"); ftio_get_ver(&ftio_in, &ftv); } setlogfile(outfile); setlogmode(logmode); if((parsetree = parse())) total_flows = extract(&ftio_in, parsetree, &ftv, &ftio_out, oldflow); else exit(1); if (logmode == LOGBINARY) { /* /* if the output file descriptor was a real file, re-write the /* flow_header with the correct # of total flows */ if (ftio_set_ver(&ftio_out, &ftv) < 0) fterr_errx(1, "ftio_set_ver(): failed"); if (out_fd_plain) { ftio_set_flows_count(&ftio_out, total_flows); ftio_set_streaming(&ftio_out, 0); if (ftio_write_header(&ftio_out) < 0) fterr_errx(1, "ftio_write_header(): failed"); } /* out_fd_plain */ /* done with output stream */ if (ftio_close(&ftio_out) < 0) fterr_errx(1, "ftio_close(): failed"); } return 0; } void helpscreen(char *name) { fprintf(stdout,"\ %s: usage: %s [options]\n\ options: -a ASCII output (the default)\n\ -B Specify endian ('big' or 'little')\n\ -b Binary output\n\ -n Use IP addresses for ASCII output\n\ -D Resolve only hosts, not services\n\ -h Print this help screen\n\ -e script Specify script inline\n\ -E script Specify script inline\n\ -f file Specify script filename\n\ -F file Specify script filename\n\ -d dumpfile Specify dump file to process (def: stdin)\n\ -o outfile Specify output file (def: stdout)\n\ -z complevel Specify compression level (def: 6)\n\ \n\ %s processes a binary dump file created by flow data from a router,\n\ selecting records to output. The syntax of the script file is similar\n\ to awk(1). The script can be specified on the command line or can be\n\ stored in a file. All scripts specified by '-e', '-E', '-f', '-F' are\n\ in effect, processed as a single file. Scripts specified by '-e' and\n\ '-f' are appended, whereas scripts specified by '-E' and '-F' are pre-\n\ pended. The use of '-E' or '-F' along with a '#!%s -f' script\n\ executable allows insertion of additional script lines from the command\n\ line.\n", name, name, name, name); } u_int64 extract(struct ftio *ftio_in, struct parsetree *pt, struct ftver *ftv, struct ftio *ftio_out, int oldflow) { char *fdata; FILE *fp; int i, n; int total_flows; total_flows = 0; while ((fdata = ftio_read(ftio_in))) { ++total_flows; protoflag = ((struct fts3rec_gen *)fdata)->prot; interp(fdata, ftv, pt, ftio_out, oldflow); } return total_flows; } void setlogfile(FILE *f) { logfile = f; } void setlogmode(int logmode) { outlogmode = logmode; } void writebuf(char *outbuf, struct ftver *ftv, int all, struct ftio *ftio_out) { switch(outlogmode){ /* BINARY always writes everything */ case LOGBINARY: writebin(outbuf, ftio_out); break; case LOGASCII: writeascii(outbuf, ftv, all); break; default: fprintf(stderr, "Internal error... unknown output mode.\n"); exit(1); break; } } void writebin(char *outbuf, struct ftio *ftio_out) { if (ftio_write(ftio_out, outbuf) < 0) fterr_errx(1, "ftio_write(): failed"); } void writeascii(char *outbuf, struct ftver *ftv, int all) { struct in_addr haddr; struct fttime ftt; struct fts3rec_gen *genrec; struct fts3rec_v1 *v1rec; struct fts3rec_v5 *v5rec; struct fts3rec_v6 *v6rec; struct fts3rec_v7 *v7rec; char fmt_buf[64]; extern char *cgethostbyaddr(struct in_addr); genrec = (struct fts3rec_gen *)outbuf; ftt = ftltime(genrec->sysUpTime, genrec->unix_secs, genrec->unix_nsecs, genrec->First); fputs(gettimestr(ftt), logfile); fputs(" -> ", logfile); genrec = (struct fts3rec_gen *)outbuf; ftt = ftltime(genrec->sysUpTime, genrec->unix_secs, genrec->unix_nsecs, genrec->Last); fputs(gettimestr(ftt), logfile); fprintf(logfile, " %4d ", genrec->prot); fprintf(logfile, " %2.2d ", genrec->input); memcpy(&haddr.s_addr, &genrec->srcaddr, 4); fmt_ipv4(fmt_buf, genrec->srcaddr, FMT_PAD_RIGHT); fprintf(logfile, "%-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); if(genrec->prot != ICMPDATA || !oldicmpstyle) { fputc(' ', logfile); fputs(getportname(genrec->srcport), logfile); } fputs(" <-> ", logfile); fprintf(logfile, " %2.2d ", genrec->output); memcpy(&haddr.s_addr, &genrec->dstaddr, 4); fmt_ipv4(fmt_buf, genrec->dstaddr, FMT_PAD_RIGHT); fprintf(logfile, "%-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); fputc(' ', logfile); fputs(getportname(genrec->dstport), logfile); fprintf(logfile, " %-10lu %-10lu ",(u_long)genrec->dPkts, (u_long)genrec->dOctets); fprintf(logfile, "%2.2x %1s%1s%1s%1s%1s%1s", (int)genrec->tos, 0x01 & genrec->tcp_flags ? "F" : "-", 0x02 & genrec->tcp_flags ? "S" : "-", 0x04 & genrec->tcp_flags ? "R" : "-", 0x08 & genrec->tcp_flags ? "P" : "-", 0x10 & genrec->tcp_flags ? "A" : "-", 0x20 & genrec->tcp_flags ? "U" : "-"); if (all) { switch (ftv->d_version) { case 1: v1rec = (struct fts3rec_v1 *)outbuf; memcpy(&haddr.s_addr, &v1rec->nexthop, 4); fmt_ipv4(fmt_buf, v1rec->nexthop, FMT_PAD_RIGHT); fprintf(logfile, " %-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); fprintf(logfile, " %-2s %-2s %-2s %-2s %-4s %-4s %-21s %-2s %-2s %-21s", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"); break; case 5: v5rec = (struct fts3rec_v5 *)outbuf; memcpy(&haddr.s_addr, &v5rec->nexthop, 4); fmt_ipv4(fmt_buf, v5rec->nexthop, FMT_PAD_RIGHT); fprintf(logfile, " %-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); fprintf(logfile, " %-2d %-2d %-2d %-2d %-4d %-4d %-21s %-2s %-2s %-21s", v5rec->engine_type, v5rec->engine_id, v5rec->src_mask, v5rec->dst_mask, v5rec->src_as, v5rec->dst_as, "-", "-", "-", "-"); break; case 6: v6rec = (struct fts3rec_v6 *)outbuf; memcpy(&haddr.s_addr, &v6rec->nexthop, 4); fmt_ipv4(fmt_buf, v6rec->nexthop, FMT_PAD_RIGHT); fprintf(logfile, " %-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); fprintf(logfile, " %-2d %-2d %-2d %-2d %-4d %-4d %-21s %-2d %-2d", v6rec->engine_type, v6rec->engine_id, v6rec->src_mask, v6rec->dst_mask, v6rec->src_as, v6rec->dst_as, "-", v6rec->in_encaps, v6rec->out_encaps); memcpy(&haddr.s_addr, &v6rec->peer_nexthop, 4); fmt_ipv4(fmt_buf, v6rec->peer_nexthop, FMT_PAD_RIGHT); fprintf(logfile, " %-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); break; case 7: v7rec = (struct fts3rec_v7 *)outbuf; memcpy(&haddr.s_addr, &v7rec->nexthop, 4); fmt_ipv4(fmt_buf, v7rec->nexthop, FMT_PAD_RIGHT); fprintf(logfile, " %-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); fprintf(logfile, " %-2d %-2d %-2d %-2d %-4d %-4d", v7rec->engine_type, v7rec->engine_id, v7rec->src_mask, v7rec->dst_mask, v7rec->src_as, v7rec->dst_as); memcpy(&haddr.s_addr, &v7rec->router_sc, 4); fmt_ipv4(fmt_buf, v7rec->router_sc, FMT_PAD_RIGHT); fprintf(logfile, " %-21s", resolve ? cgethostbyaddr(haddr) : fmt_buf); fprintf(logfile, " %-2s %-2s %-21s", "-", "-", "-"); break; } } fputc('\n', logfile); fflush(logfile); } char * getportname(unsigned short p) { struct servent *se; static char result[80]; if(protoflag == ICMPDATA) return geticmpportname(p); if(srvresolve && (se = getservbyport((unsigned int)ntohs(p), protoflag == TCPDATA ? "tcp" : "udp"))) strcpy(result, se->s_name); else sprintf(result, "%u", (unsigned int)ntohs(p)); return result; } static char *icmpmsgs[] = { "echorep", /* 0 */ "icmp1", /* 1 */ "icmp2", /* 2 */ "unreachable", /* 3 */ "srcquench", /* 4 */ "redirect", /* 5 */ "icmp6", /* 6 */ "icmp7", /* 7 */ "echoreq", /* 8 */ "icmp9", "icmp10", "timex", "badhdr", "tstampreq", "tstamprep", "inforeq", "inforep", "maskreq", "maskrep", 0 }; static char *icmpunreach[] = { "netunreach", "hostunreach", "protounreach", "portunreach", "fraglost", "badsrcroute", 0 }; static char *icmpredir[] = { "netredir", "hostredir", "tosnetredir", "toshostredir", 0 }; char * geticmpportname(unsigned short p) { static char result[80]; int type, subtype; type = (p & 0xff00) >> 8; subtype = p&0xff; if(srvresolve && type <= ICMP_MAXTYPE){ switch(type){ case ICMP_UNREACH: if(subtype < 6) strcpy(result, icmpunreach[subtype]); else sprintf(result, "unreach_%d", subtype); break; case ICMP_REDIRECT: if(subtype < 4) strcpy(result, icmpredir[subtype]); else sprintf(result, "redirect_%d", subtype); break; default: strcpy(result, icmpmsgs[type]); break; } } else sprintf(result, "%u.%u", type, subtype); return result; }