📄 gulp.c
字号:
if (check_block) { if (would_block) fprintf(stderr, "select reports writes would have blocked\n"); else fprintf(stderr, "select reports writes would not have blocked\n"); } /* cleanup */ pcap_freecode(&fp);#ifndef RHEL3 pcap_close(handle);#endif /* RHEL3 */ }#endif /* JUSTCOPY */ fprintf(stderr, "ring buffer use: %.1lf%% of %d MB\n", 100.0*(double)maxbuffered/(double)(ringsize), ringsize/1024/1024); eof = 1; fflush(stderr); pthread_exit(NULL); }/* * Redirect standard output into a new capture file in the specified directory. * * In case Gulp is running setuid root, try to prevent a user from * overwriting system files. This is accomplished by creating output files * with random temporary names in a directory to which the user has write * access and subsequently renaming them to names unlikely to cause trouble. */intnewoutfile(char *dir, int num) { char tfile[PATH_MAX]; /* output temp filename */ char ofile[PATH_MAX]; /* output real filename */ if (access(dir, W_OK) != 0) { if (access(dir, F_OK) != 0) { fprintf(stderr, "%s: -o dir does not exist: '%s'\n", progname, dir); return (0); } fprintf(stderr, "%s: can't create files in '%s'\n", progname, dir); return (0); } snprintf(tfile, sizeof(tfile), "%s%s", dir, TEMPLATE); snprintf(ofile, sizeof(ofile), "%s/pcap%03d", dir, num); int tmpfd = mkstemp(tfile); fchown(tmpfd, getuid(), -1); /* in case running setuid */ if (tmpfd >= 0) { if (freopen(tfile, "w", stdout) == NULL) { fprintf(stderr, "%s: can't create output file: '%s'\n", progname, tfile); return (0); } dup2(tmpfd, fileno(stdout)); /* try to use the initial fd */ close(tmpfd); rename(tfile, ofile); return (1); } else { fprintf(stderr, "%s: can't create: '%s'\n", progname, tfile); return(0); } return (0); /* some error */ }/* * This thread copies the ring buffer to stdout in WriteSize chunks * or every second (or so) whichever happens first. */void *Writer(void *arg) { int n; int used; int writesize; int done = 0; int pushed = 0; /* value of "push" at last write */#ifdef CPU_SET int wtid = gettid(); /* Writer thread id */ cpu_set_t csmask; CPU_ZERO(&csmask); CPU_SET(WRITER_CPU, &csmask); if (my_sched_setaffinity(wtid, sizeof(cpu_set_t), &csmask) != 0) { fprintf(stderr, "%s: Writer could not set cpu affinity: %s\n", progname, strerror(errno)); } if (setpriority(PRIO_PROCESS, wtid, WRITE_PRIO) != 0) { fprintf(stderr, "%s: Writer could not set scheduling priority: %s\n", progname, strerror(errno)); }#else replace with equivalent code for your OS or delete and run less optimally#endif /* CPU_SET */ if (geteuid()!=getuid()) { while (!reader_ready) usleep(poll_usecs); seteuid(getuid()); /* drop setuid privilege */ } if (odir && !newoutfile(odir, filec++)) { exit(1); } while (!done) { used = end - start; if (used < 0) used += ringsize; if (start & (WriteSize-1)) writesize = WriteSize - (start & (WriteSize-1)); /* re-align */ else writesize = WriteSize; while (used < WriteSize) { if (eof) { done = 1; used = end - start; if (used < 0) used += ringsize; writesize = used; break; } else if (push > pushed+1) { writesize = used; if (used) break; } usleep(poll_usecs); used = end - start; if (used < 0) used += ringsize; } n = ringsize - start; /* short write at end of ring? */ if (n < writesize) writesize = n; /* write remainder next loop */ if (check_block) { /* * this is mostly of academic interest */ fd_set w_set; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO(&w_set); FD_SET(1, &w_set); if (select(2, NULL, &w_set, NULL, &timeout) != -1) { if (!FD_ISSET(1, &w_set)) { would_block = 1; if (yield_if_blocking) { writesize = 0; /* next iteration will try again */ sched_yield(); } } } } if (writesize > 0) { if (start < boundary && start+writesize >= boundary) { writesize = boundary - start; } writesize = write(1, buf+start, writesize); } if (writesize == -1 && errno == EINTR) writesize = 0; if (writesize < 0) { fprintf(stderr, "%s: fatal write error: %s\n", progname, strerror(errno)); eof = 1; fflush(stderr); pthread_exit(0); } start += (start+writesize >= ringsize) ? writesize-ringsize : writesize; if (start == boundary) { if (max_files && filec >= max_files) filec = 0; newoutfile(odir, filec++); boundary = -2; } pushed = push; } pthread_exit(NULL); }voidusage() { fprintf(stderr, "\n" "Usage: %s [--help | options]\n" " --help\tprints this usage summary\n" " supported options include:\n"#ifdef JUSTCOPY " (This binary was compiled with JUSTCOPY so some options are unavailable)\n"#else /* JUSTCOPY */ " -d\tdecapsulate Cisco ERSPAN GRE packets (sets -f value)\n" " -f \"...\"\tspecify a pcap filter - see manpage and -d\n" " -i eth#|-\tspecify ethernet capture interface or '-' for stdin\n" " -s #\tspecify packet capture \"snapshot\" length limit\n"#endif /* JUSTCOPY */ " -r #\tspecify ring buffer size in megabytes (1-1024)\n" " -c\tjust buffer stdin to stdout (works with arbitrary data)\n" " -x\trequest exclusive lock (to be the only instance running)\n" " -X\trun even when locking would forbid it\n" " -v\tprint program version and exit\n" " -Vx...x\tdisplay packet loss and buffer use - see manpage\n" " -p #\tspecify full/empty polling interval in microseconds\n" " -q\tsuppress buffer full warnings\n" " -z #\tspecify write blocksize (even power of 2, default 65536)\n" " for long-term capture\n" " -o dir\tredirect pcap output to a collection of files in dir\n" " -C #\tlimit each pcap file in -o dir to # times the (-r #) size\n" " -W #\toverwrite pcap files in -o dir rather than start #+1\n" " and some of academic interest only:\n" " -B\tcheck if select(2) would ever have blocked on write\n" " -Y\tavoid writes which would block\n" "\n", progname); }/* * This thread starts the other two and then wakes every half second * to increment a variable the writer uses to decide if it should flush. * Flushing greatly facilitates interactive use and testing tcpdump filters. */int main(int argc, char *argv[], char *envp[]) { pthread_t threads[2]; int rc, t, c, errflag=0; extern char *optarg; extern int optind; int bitmask; start = end = eof = 0; progname = argv[0];#ifdef JUSTCOPY just_copy = 1;#endif /* pick up default interface to sniff from ENV if present */ if (getenv("CAP_IFACE")) dev = getenv("CAP_IFACE"); if (argc > 1 && strcmp(argv[1], "--help") == 0) ++errflag; else#ifndef JUSTCOPY while ((c = getopt(argc, argv, "BXYcdqvxf:i:p:r:s:z:V:o:C:W:")) != EOF)#else /* JUSTCOPY */ while ((c = getopt(argc, argv, "BXYcqvxp:r:z:V:o:C:W:")) != EOF)#endif /* JUSTCOPY */ { switch (c) { case 'B': check_block = 1; /* use select to avoid write blocking */ break; case 'Y': check_block = 1; yield_if_blocking = 1; /* don't issue blocking writes */ break; case 'V': /* produce periodic drop,ring stats */ ps_stat_ptr = optarg; if (ps_stat_ptr[0] == '-') { fprintf(stderr, "%s: %s is suspicious as argument of -V\n", progname, ps_stat_ptr); errflag++; } ps_stat_len = strlen(ps_stat_ptr); break; case 'c': just_copy = 1; /* just read from stdin and buffer */ break; case 'd': gre_hdrlen = GRE_HDRLEN;/* decapsulate Cisco GRE */ filter_exp = "proto gre"; break; case 'f': filter_exp = optarg; break; case 'i': /* specify ethernet device name */ dev = optarg; break; case 'p': /* specify polling sleep u_secs */ t = atoi(optarg); if (t < 0 || t > 1000000) { fprintf(stderr, "%s: -p number must be 0-1000000\n", progname); ++errflag; } else poll_usecs = t; break; case 'q': /* warnings can be annoying */ warn_buf_full = 0; break; case 'r': t = atoi(optarg); /* specify ring size in MB */ if (t < 1 || t > 1024) { fprintf(stderr, "%s: -r number must be 1-1024\n", progname); ++errflag; } else ringsize = t * 1024*1024; break; case 's': /* specify snapshot length */ t = atoi(optarg); if (t <= 0 || t > SNAP_LEN) t = SNAP_LEN; snap_len = t; break; case 'v': fprintf(stderr, "%s\n", id+5); exit(0); break; case 'x': xlock = 1; /* request exclusive lock */ break; case 'X': xlock = -1; /* disregard locking conflicts */ break; case 'z': t = atoi(optarg); /* specify goal write size 2^n */ for (bitmask=1; bitmask<=65536; bitmask*=2) { if (t == bitmask) WriteSize = t; } if (WriteSize != t) { fprintf(stderr, "%s: -z number must be a power of 2\n", progname); errflag++; } break; case 'o': odir = optarg; if (strlen(odir) >= PATH_MAX-strlen(TEMPLATE)-1) { fprintf(stderr, "%s: -o name too long: %s\n", progname, odir); errflag++; } break; case 'C': split_after = atoi(optarg); if (split_after < 1) { fprintf(stderr, "%s: -C # must be 1 or greater\n", progname); errflag++; } break; case 'W': max_files = atoi(optarg); if (max_files < 1) { fprintf(stderr, "%s: -W # must be 1 or greater\n", progname); errflag++; } break; default: errflag++; break; } } if (errflag || optind < argc) { usage(); exit(1); } /* * if -d is spcified, -s refers to decapsulated sizes, make it happen */ d_snap_len = snap_len + gre_hdrlen; if (d_snap_len <= 0 || d_snap_len > SNAP_LEN) d_snap_len = SNAP_LEN; if (isatty(1) && !just_copy && !odir) { fprintf(stderr, "%s:\tSending raw pcap data to a terminal is not a " "good idea.\n\tIf you really want to do that, pipe %s through " "cat but you\n\tprobably want to redirect stdout to a file or " "another program instead.\n\tPerhaps you meant to pipe into " "'tcpdump -r-' or 'ngrep -I-' ?\n", progname, progname); if (argc == 1) usage(); exit(1); } /* * Advisory locking logic */ if ((lockfd = open("/proc/self/exe", O_RDONLY)) < 0) { fprintf(stderr, "%s: Warning: couldn't open lockfile so not locking\n", progname); } else { if (flock(lockfd, ((xlock==1 ? LOCK_EX : LOCK_SH) | LOCK_NB)) == -1) { if (xlock < 0) { fprintf(stderr, "%s: Warning: overriding locking\n", progname); } else { fprintf(stderr, "%s: Exiting due to lock conflict\n", progname); exit(1); } } } buf = malloc(ringsize+1); if (!buf) { fprintf(stderr, "%s: Malloc failed, exiting\n", progname); exit(1); } if (mlock(buf, ringsize+1) != 0) { fprintf(stderr, "%s: Warning: could not lock ring buffer into RAM\n", progname); } rc = pthread_create(&threads[0], NULL, &Reader, NULL); if (rc){ fprintf(stderr, "%s: pthread_create error\n", progname); exit(1); } rc = pthread_create(&threads[1], NULL, &Writer, NULL); if (rc){ fprintf(stderr, "%s: pthread_create error\n", progname); exit(1); } while (!eof) { usleep(500000); push += 1; /* * emit some stats which may be useful while testing * if argument to -V is big enough to write into, do so * else write to stdout. */ if (ps_stat_ptr) { char sbuf[V_WIDTH+1]; int drop_symb = 0; int used = end - start; if (used < 0) used += ringsize;#ifndef JUSTCOPY if (handle && pcap_stats(handle, &pcs) >= 0) { int d = pcs.ps_drop; /* count how many decimal digits are in the drop count */ for (drop_symb = 0; drop_symb < 9; ++drop_symb) { if (d == 0) break; d /= 10; } }#endif /* JUSTCOPY */ if (ps_stat_len >= V_WIDTH) { /* put stats in arg list */ sprintf(sbuf, "%1.1d %.0lf,%.0lf%%", drop_symb, /* a digit from 0-9 */ 100.0*(double)used/(double)(ringsize), 100.0*(double)maxbuffered/(double)(ringsize)); sprintf(ps_stat_ptr, "%-*s", ps_stat_len, sbuf); } else { /* puts stats on stderr */ fprintf(stderr, "pkts dropped: %d, ring buf: %.1lf%%, max: %.1lf%%\n", (drop_symb > 0 ? pcs.ps_drop : 0), 100.0*(double)used/(double)(ringsize), 100.0*(double)maxbuffered/(double)(ringsize)); } } } fflush(stderr); pthread_exit(NULL); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -