This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
common-tools-tcpdump-mesa/src/tcpdump.c

3382 lines
92 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Support for splitting captures into multiple files with a maximum
* file size:
*
* Copyright (c) 2001
* Seth Webster <swebster@sst.ll.mit.edu>
*/
/* 2016-11-29 lijia add,
类似FTP方式, TCP连接传输命令, UDP连接传输实际捕包.
1-随机打开本端UDP未用端口, 默认12345, 如被占用, 顺序后延;
2-与sapp建立TCP连接, 发送本端UDP监听端口;
3-给sapp发送捕包控制命令, 传输BPF-filter过滤字符串,
4-从UDP端口读取sapp捕获的数据包;
5-调用tcpdump原版流程, 解析打印或写文件。
2018-01-19 lijia add,
1-多个tcpdump_mesa同时启动时, 后续的连接会抢占第一个连接的数据流, 但使用第一个连接的过滤条件,
增加TCP命令连接的确认机制, 如果sapp不回复确认包, tcpdump_mesa不启动捕包.
2-增加丢包计数, 如果使用-a参数指定perceptive模式, sapp在发送包时, 在源MAC地址上打上序号,
tcpdump_mesa检查序号是否连续, 以确认中间是否有丢包, 丢了几个包.
*/
#define MESA_DUMP (1)
#if MESA_DUMP
#include "mesa_pkt_dump.h"
int tcpdump_data_offset = 0; /* 用于跳过某些底层数据, 如vxlan, 可以直接获取或设置过滤条件看vxlan的内层数据包内容 */
unsigned char tcpdump_thread_index_array[64]; /* 开启捕包线程id数组, 靠长度决定id数量, 每个占1字节, 命令行输入支持逗号分隔 */
int tcpdump_thread_index_array_num = 0;
const char *tcpdump_thread_index_str;
int tcpdump_perceptive_flag = 0;
unsigned int perceptive_pkt_seq[256]; /* 最大支持256个线程 */
static int greedy_seek_flag = 0; /* 偏移到最内层IP, 便于隧道模式下查找BUG */
static int dump_to_file_flag = 0; /* 是否有-w 参数, 原有标准的WFileName变量是main()的局部变量, 不方便使用, 使用此变量表示是否写文件 */
static int has_device_flag = 0; /* 是否有-i, -r参数, 原有标准的device变量是main()的局部变量, 不方便使用, 使用此变量表示是否从某个网卡捕包 */
static int has_bpf_filter_flag = 0; /* 是否有正确的BPF过滤条件 */
extern int treat_vlan_as_mac_in_mac_sw;
static short pkt_classify_flag = 0;
static char pkt_classify_watermark_sw = 0;
int tcpdump_r_offline_mode = 0; /* 从pcap文件里读包, 而不是来源于sapp, 主要是应对有-o参数时处理模式不一样 */
#endif
#ifndef lint
static const char copyright[] _U_ =
"@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
The Regents of the University of California. All rights reserved.\n";
#endif
/*
* tcpdump - dump traffic on a network
*
* First written in 1987 by Van Jacobson, Lawrence Berkeley Laboratory.
* Mercilessly hacked and occasionally improved since then via the
* combined efforts of Van, Steve McCanne and Craig Leres of LBL.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/*
* Mac OS X may ship pcap.h from libpcap 0.6 with a libpcap based on
* 0.8. That means it has pcap_findalldevs() but the header doesn't
* define pcap_if_t, meaning that we can't actually *use* pcap_findalldevs().
*/
#ifdef HAVE_PCAP_FINDALLDEVS
#ifndef HAVE_PCAP_IF_T
#undef HAVE_PCAP_FINDALLDEVS
#endif
#endif
#include <netdissect-stdinc.h>
#ifdef USE_LIBSMI
#include <smi.h>
#endif
#ifdef HAVE_LIBCRYPTO
#include <openssl/crypto.h>
#endif
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#else
#include "getopt_long.h"
#endif
/* Capsicum-specific code requires macros from <net/bpf.h>, which will fail
* to compile if <pcap.h> has already been included; including the headers
* in the opposite order works fine.
*/
#ifdef HAVE_CAPSICUM
#include <sys/capability.h>
#include <sys/ioccom.h>
#include <net/bpf.h>
#include <fcntl.h>
#include <libgen.h>
#endif /* HAVE_CAPSICUM */
#include <pcap.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#ifndef _WIN32
#include <sys/wait.h>
#include <sys/resource.h>
#include <pwd.h>
#include <grp.h>
#endif /* _WIN32 */
/* capabilities convenience library */
/* If a code depends on HAVE_LIBCAP_NG, it depends also on HAVE_CAP_NG_H.
* If HAVE_CAP_NG_H is not defined, undefine HAVE_LIBCAP_NG.
* Thus, the later tests are done only on HAVE_LIBCAP_NG.
*/
#ifdef HAVE_LIBCAP_NG
#ifdef HAVE_CAP_NG_H
#include <cap-ng.h>
#else
#undef HAVE_LIBCAP_NG
#endif /* HAVE_CAP_NG_H */
#endif /* HAVE_LIBCAP_NG */
#include "netdissect.h"
#include "interface.h"
#include "addrtoname.h"
#include "machdep.h"
#include "setsignal.h"
#include "gmt2local.h"
#include "pcap-missing.h"
#include "ascii_strcasecmp.h"
#include "print.h"
#ifdef GIT_VERSION
const char *tcpdump_mesa_version = (const char *)GIT_VERSION;
#else
const char *tcpdump_mesa_version = "GIT_VERSION_UNKNOWN";
#endif
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#ifdef SIGINFO
#define SIGNAL_REQ_INFO SIGINFO
#elif SIGUSR1
#define SIGNAL_REQ_INFO SIGUSR1
#endif
static int Cflag; /* rotate dump files after this many bytes */
static int Cflag_count; /* Keep track of which file number we're writing */
static int Dflag; /* list available devices and exit */
/*
* This is exported because, in some versions of libpcap, if libpcap
* is built with optimizer debugging code (which is *NOT* the default
* configuration!), the library *imports*(!) a variable named dflag,
* under the expectation that tcpdump is exporting it, to govern
* how much debugging information to print when optimizing
* the generated BPF code.
*
* This is a horrible hack; newer versions of libpcap don't import
* dflag but, instead, *if* built with optimizer debugging code,
* *export* a routine to set that flag.
*/
int dflag; /* print filter code */
static int Gflag; /* rotate dump files after this many seconds */
static int Gflag_count; /* number of files created with Gflag rotation */
static time_t Gflag_time; /* The last time_t the dump file was rotated. */
static int Lflag; /* list available data link types and exit */
static int Iflag; /* rfmon (monitor) mode */
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
static int Jflag; /* list available time stamp types */
#endif
#ifdef HAVE_PCAP_SETDIRECTION
int Qflag = -1; /* restrict captured packet by send/receive direction */
#endif
static int Uflag; /* "unbuffered" output of dump files */
static int Wflag; /* recycle output files after this number of files */
static int WflagChars;
static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */
static int infodelay;
static int infoprint;
char *program_name;
/* Forwards */
static RETSIGTYPE cleanup(int);
static RETSIGTYPE child_cleanup(int);
static void print_version(void);
static void print_usage(void);
static void show_tstamp_types_and_exit(const char *device) __attribute__((noreturn));
static void show_dlts_and_exit(const char *device) __attribute__((noreturn));
static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *);
static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
static void droproot(const char *, const char *);
static void MESA_dump_print_packet(unsigned char *user, const struct pcap_pkthdr *h, const unsigned char *pkt);
static void MESA_dump_packet(unsigned char *user, const struct pcap_pkthdr *h, const unsigned char *raw_pkt);
#ifdef SIGNAL_REQ_INFO
RETSIGTYPE requestinfo(int);
#endif
#if defined(USE_WIN32_MM_TIMER)
#include <MMsystem.h>
static UINT timer_id;
static void CALLBACK verbose_stats_dump(UINT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR);
#elif defined(HAVE_ALARM)
static void verbose_stats_dump(int sig);
#endif
static void info(int);
static u_int packets_captured;
static const struct tok status_flags[] = {
#ifdef PCAP_IF_UP
{ PCAP_IF_UP, "Up" },
#endif
#ifdef PCAP_IF_RUNNING
{ PCAP_IF_RUNNING, "Running" },
#endif
{ PCAP_IF_LOOPBACK, "Loopback" },
{ 0, NULL }
};
static pcap_t *pd;
static int supports_monitor_mode;
extern int optind;
extern int opterr;
extern char *optarg;
struct dump_info {
char *WFileName;
char *CurrentFileName;
pcap_t *pd;
pcap_dumper_t *p;
#ifdef HAVE_CAPSICUM
int dirfd;
#endif
};
#if defined(HAVE_PCAP_SET_PARSER_DEBUG)
/*
* We have pcap_set_parser_debug() in libpcap; declare it (it's not declared
* by any libpcap header, because it's a special hack, only available if
* libpcap was configured to include it, and only intended for use by
* libpcap developers trying to debug the parser for filter expressions).
*/
#ifdef _WIN32
__declspec(dllimport)
#else /* _WIN32 */
extern
#endif /* _WIN32 */
void pcap_set_parser_debug(int);
#elif defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG)
/*
* We don't have pcap_set_parser_debug() in libpcap, but we do have
* pcap_debug or yydebug. Make a local version of pcap_set_parser_debug()
* to set the flag, and define HAVE_PCAP_SET_PARSER_DEBUG.
*/
static void
pcap_set_parser_debug(int value)
{
#ifdef HAVE_PCAP_DEBUG
extern int pcap_debug;
pcap_debug = value;
#else /* HAVE_PCAP_DEBUG */
extern int yydebug;
yydebug = value;
#endif /* HAVE_PCAP_DEBUG */
}
#define HAVE_PCAP_SET_PARSER_DEBUG
#endif
#if defined(HAVE_PCAP_SET_OPTIMIZER_DEBUG)
/*
* We have pcap_set_optimizer_debug() in libpcap; declare it (it's not declared
* by any libpcap header, because it's a special hack, only available if
* libpcap was configured to include it, and only intended for use by
* libpcap developers trying to debug the optimizer for filter expressions).
*/
#ifdef _WIN32
__declspec(dllimport)
#else /* _WIN32 */
extern
#endif /* _WIN32 */
void pcap_set_optimizer_debug(int);
#endif
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
static void
show_tstamp_types_and_exit(const char *device)
{
int n_tstamp_types;
int *tstamp_types = 0;
const char *tstamp_type_name;
int i;
n_tstamp_types = pcap_list_tstamp_types(pd, &tstamp_types);
if (n_tstamp_types < 0)
error("%s", pcap_geterr(pd));
if (n_tstamp_types == 0) {
fprintf(stderr, "Time stamp type cannot be set for %s\n",
device);
exit(0);
}
fprintf(stderr, "Time stamp types for %s (use option -j to set):\n",
device);
for (i = 0; i < n_tstamp_types; i++) {
tstamp_type_name = pcap_tstamp_type_val_to_name(tstamp_types[i]);
if (tstamp_type_name != NULL) {
(void) fprintf(stderr, " %s (%s)\n", tstamp_type_name,
pcap_tstamp_type_val_to_description(tstamp_types[i]));
} else {
(void) fprintf(stderr, " %d\n", tstamp_types[i]);
}
}
pcap_free_tstamp_types(tstamp_types);
exit(0);
}
#endif
static void
show_dlts_and_exit(const char *device)
{
int n_dlts, i;
int *dlts = 0;
const char *dlt_name;
n_dlts = pcap_list_datalinks(pd, &dlts);
if (n_dlts < 0)
error("%s", pcap_geterr(pd));
else if (n_dlts == 0 || !dlts)
error("No data link types.");
/*
* If the interface is known to support monitor mode, indicate
* whether these are the data link types available when not in
* monitor mode, if -I wasn't specified, or when in monitor mode,
* when -I was specified (the link-layer types available in
* monitor mode might be different from the ones available when
* not in monitor mode).
*/
if (supports_monitor_mode)
(void) fprintf(stderr, "Data link types for %s %s (use option -y to set):\n",
device,
Iflag ? "when in monitor mode" : "when not in monitor mode");
else
(void) fprintf(stderr, "Data link types for %s (use option -y to set):\n",
device);
for (i = 0; i < n_dlts; i++) {
dlt_name = pcap_datalink_val_to_name(dlts[i]);
if (dlt_name != NULL) {
(void) fprintf(stderr, " %s (%s)", dlt_name,
pcap_datalink_val_to_description(dlts[i]));
/*
* OK, does tcpdump handle that type?
*/
if (!has_printer(dlts[i]))
(void) fprintf(stderr, " (printing not supported)");
fprintf(stderr, "\n");
} else {
(void) fprintf(stderr, " DLT %d (printing not supported)\n",
dlts[i]);
}
}
#ifdef HAVE_PCAP_FREE_DATALINKS
pcap_free_datalinks(dlts);
#endif
exit(0);
}
#ifdef HAVE_PCAP_FINDALLDEVS
static void
show_devices_and_exit (void)
{
pcap_if_t *dev, *devlist;
char ebuf[PCAP_ERRBUF_SIZE];
int i;
if (pcap_findalldevs(&devlist, ebuf) < 0)
error("%s", ebuf);
for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) {
printf("%d.%s", i+1, dev->name);
if (dev->description != NULL)
printf(" (%s)", dev->description);
if (dev->flags != 0)
printf(" [%s]", bittok2str(status_flags, "none", dev->flags));
printf("\n");
}
pcap_freealldevs(devlist);
exit(0);
}
#endif /* HAVE_PCAP_FINDALLDEVS */
/*
* Short options.
*
* Note that there we use all letters for short options except for g, k,
* o, and P, and those are used by other versions of tcpdump, and we should
* only use them for the same purposes that the other versions of tcpdump
* use them:
*
* OS X tcpdump uses -g to force non--v output for IP to be on one
* line, making it more "g"repable;
*
* OS X tcpdump uses -k tospecify that packet comments in pcap-ng files
* should be printed;
*
* OpenBSD tcpdump uses -o to indicate that OS fingerprinting should be done
* for hosts sending TCP SYN packets;
*
* OS X tcpdump uses -P to indicate that -w should write pcap-ng rather
* than pcap files.
*
* OS X tcpdump also uses -Q to specify expressions that match packet
* metadata, including but not limited to the packet direction.
* The expression syntax is different from a simple "in|out|inout",
* and those expressions aren't accepted by OS X tcpdump, but the
* equivalents would be "in" = "dir=in", "out" = "dir=out", and
* "inout" = "dir=in or dir=out", and the parser could conceivably
* special-case "in", "out", and "inout" as expressions for backwards
* compatibility, so all is not (yet) lost.
*/
/*
* Set up flags that might or might not be supported depending on the
* version of libpcap we're using.
*/
#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
#define B_FLAG "B:"
#define B_FLAG_USAGE " [ -B size ]"
#else /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
#define B_FLAG
#define B_FLAG_USAGE
#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
#ifdef HAVE_PCAP_CREATE
#define I_FLAG "I"
#else /* HAVE_PCAP_CREATE */
#define I_FLAG
#endif /* HAVE_PCAP_CREATE */
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
#define j_FLAG "j:"
#define j_FLAG_USAGE " [ -j tstamptype ]"
#define J_FLAG "J"
#else /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */
#define j_FLAG
#define j_FLAG_USAGE
#define J_FLAG
#endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */
#ifdef HAVE_PCAP_FINDALLDEVS
#define D_FLAG "D"
#else
#define D_FLAG
#endif
#ifdef HAVE_PCAP_DUMP_FLUSH
#define U_FLAG "U"
#else
#define U_FLAG
#endif
#ifdef HAVE_PCAP_SETDIRECTION
#define Q_FLAG "Q:"
#else
#define Q_FLAG
#endif
#if MESA_DUMP /* lijia add, 新增参数g, k, o, P */
#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:gG:hHi:" I_FLAG j_FLAG J_FLAG "k:KlLm:M:nNo:OP:pq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#"
#else
#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#"
#endif
/*
* Long options.
*
* We do not currently have long options corresponding to all short
* options; we should probably pick appropriate option names for them.
*
* However, the short options where the number of times the option is
* specified matters, such as -v and -d and -t, should probably not
* just map to a long option, as saying
*
* tcpdump --verbose --verbose
*
* doesn't make sense; it should be --verbosity={N} or something such
* as that.
*
* For long options with no corresponding short options, we define values
* outside the range of ASCII graphic characters, make that the last
* component of the entry for the long option, and have a case for that
* option in the switch statement.
*/
#define OPTION_VERSION 128
#define OPTION_TSTAMP_PRECISION 129
#define OPTION_IMMEDIATE_MODE 130
#if MESA_DUMP
#define OPTION_VLAN_AS_MAC_IN_MAC 131 /* 短参数不够用了, 增加长参数 */
#define OPTION_PKT_CLASSIFY 132 /* 增加长参数包类型定义见PKT_DUMP_OPT_CLASSIFY */
#define OPTION_PKT_CLASSIFY_WATERMARK 133 /* PKT_DUMP_OPT_CLASSIFY_WATERMARK */
#endif
static const struct option longopts[] = {
#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
{ "buffer-size", required_argument, NULL, 'B' },
#endif
{ "list-interfaces", no_argument, NULL, 'D' },
{ "help", no_argument, NULL, 'h' },
{ "interface", required_argument, NULL, 'i' },
#ifdef HAVE_PCAP_CREATE
{ "monitor-mode", no_argument, NULL, 'I' },
#endif
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
{ "time-stamp-type", required_argument, NULL, 'j' },
{ "list-time-stamp-types", no_argument, NULL, 'J' },
#endif
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
{ "time-stamp-precision", required_argument, NULL, OPTION_TSTAMP_PRECISION},
#endif
{ "dont-verify-checksums", no_argument, NULL, 'K' },
{ "list-data-link-types", no_argument, NULL, 'L' },
{ "no-optimize", no_argument, NULL, 'O' },
{ "no-promiscuous-mode", no_argument, NULL, 'p' },
#ifdef HAVE_PCAP_SETDIRECTION
{ "direction", required_argument, NULL, 'Q' },
#endif
{ "snapshot-length", required_argument, NULL, 's' },
{ "absolute-tcp-sequence-numbers", no_argument, NULL, 'S' },
#ifdef HAVE_PCAP_DUMP_FLUSH
{ "packet-buffered", no_argument, NULL, 'U' },
#endif
{ "linktype", required_argument, NULL, 'y' },
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
{ "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE },
#endif
#ifdef HAVE_PCAP_SET_PARSER_DEBUG
{ "debug-filter-parser", no_argument, NULL, 'Y' },
#endif
{ "relinquish-privileges", required_argument, NULL, 'Z' },
{ "number", no_argument, NULL, '#' },
{ "version", no_argument, NULL, OPTION_VERSION },
#if MESA_DUMP
{ "vlan-as-mac-in-mac", no_argument, NULL, OPTION_VLAN_AS_MAC_IN_MAC },
{ "classify", required_argument, NULL, OPTION_PKT_CLASSIFY },
{ "enable_classify_watermark", no_argument, NULL, OPTION_PKT_CLASSIFY_WATERMARK },
#endif
{ NULL, 0, NULL, 0 }
};
#ifndef _WIN32
/* Drop root privileges and chroot if necessary */
static void
droproot(const char *username, const char *chroot_dir)
{
struct passwd *pw = NULL;
if (chroot_dir && !username) {
fprintf(stderr, "%s: Chroot without dropping root is insecure\n",
program_name);
exit(1);
}
pw = getpwnam(username);
if (pw) {
if (chroot_dir) {
if (chroot(chroot_dir) != 0 || chdir ("/") != 0) {
fprintf(stderr, "%s: Couldn't chroot/chdir to '%.64s': %s\n",
program_name, chroot_dir, pcap_strerror(errno));
exit(1);
}
}
#ifdef HAVE_LIBCAP_NG
{
int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG);
if (ret < 0) {
fprintf(stderr, "error : ret %d\n", ret);
} else {
fprintf(stderr, "dropped privs to %s\n", username);
}
}
#else
if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) {
fprintf(stderr, "%s: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n",
program_name, username,
(unsigned long)pw->pw_uid,
(unsigned long)pw->pw_gid,
pcap_strerror(errno));
exit(1);
}
else {
fprintf(stderr, "dropped privs to %s\n", username);
}
#endif /* HAVE_LIBCAP_NG */
}
else {
fprintf(stderr, "%s: Couldn't find user '%.32s'\n",
program_name, username);
exit(1);
}
#ifdef HAVE_LIBCAP_NG
/* We don't need CAP_SETUID and CAP_SETGID any more. */
capng_updatev(
CAPNG_DROP,
CAPNG_EFFECTIVE | CAPNG_PERMITTED,
CAP_SETUID,
CAP_SETGID,
-1);
capng_apply(CAPNG_SELECT_BOTH);
#endif /* HAVE_LIBCAP_NG */
}
#endif /* _WIN32 */
static int
getWflagChars(int x)
{
int c = 0;
x -= 1;
while (x > 0) {
c += 1;
x /= 10;
}
return c;
}
static void
MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars)
{
char *filename = malloc(PATH_MAX + 1);
if (filename == NULL)
error("Makefilename: malloc");
/* Process with strftime if Gflag is set. */
if (Gflag != 0) {
struct tm *local_tm;
/* Convert Gflag_time to a usable format */
if ((local_tm = localtime(&Gflag_time)) == NULL) {
error("MakeTimedFilename: localtime");
}
/* There's no good way to detect an error in strftime since a return
* value of 0 isn't necessarily failure.
*/
strftime(filename, PATH_MAX, orig_name, local_tm);
} else {
strncpy(filename, orig_name, PATH_MAX);
}
if (cnt == 0 && max_chars == 0)
strncpy(buffer, filename, PATH_MAX + 1);
else
if (snprintf(buffer, PATH_MAX + 1, "%s%0*d", filename, max_chars, cnt) > PATH_MAX)
/* Report an error if the filename is too large */
error("too many output files or filename is too long (> %d)", PATH_MAX);
free(filename);
}
static char *
get_next_file(FILE *VFile, char *ptr)
{
char *ret;
ret = fgets(ptr, PATH_MAX, VFile);
if (!ret)
return NULL;
if (ptr[strlen(ptr) - 1] == '\n')
ptr[strlen(ptr) - 1] = '\0';
return ret;
}
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
static int
tstamp_precision_from_string(const char *precision)
{
if (strncmp(precision, "nano", strlen("nano")) == 0)
return PCAP_TSTAMP_PRECISION_NANO;
if (strncmp(precision, "micro", strlen("micro")) == 0)
return PCAP_TSTAMP_PRECISION_MICRO;
return -EINVAL;
}
static const char *
tstamp_precision_to_string(int precision)
{
switch (precision) {
case PCAP_TSTAMP_PRECISION_MICRO:
return "micro";
case PCAP_TSTAMP_PRECISION_NANO:
return "nano";
default:
return "unknown";
}
}
#endif
#ifdef HAVE_CAPSICUM
/*
* Ensure that, on a dump file's descriptor, we have all the rights
* necessary to make the standard I/O library work with an fdopen()ed
* FILE * from that descriptor.
*
* A long time ago, in a galaxy far far away, AT&T decided that, instead
* of providing separate APIs for getting and setting the FD_ flags on a
* descriptor, getting and setting the O_ flags on a descriptor, and
* locking files, they'd throw them all into a kitchen-sink fcntl() call
* along the lines of ioctl(), the fact that ioctl() operations are
* largely specific to particular character devices but fcntl() operations
* are either generic to all descriptors or generic to all descriptors for
* regular files nonwithstanding.
*
* The Capsicum people decided that fine-grained control of descriptor
* operations was required, so that you need to grant permission for
* reading, writing, seeking, and fcntl-ing. The latter, courtesy of
* AT&T's decision, means that "fcntl-ing" isn't a thing, but a motley
* collection of things, so there are *individual* fcntls for which
* permission needs to be granted.
*
* The FreeBSD standard I/O people implemented some optimizations that
* requires that the standard I/O routines be able to determine whether
* the descriptor for the FILE * is open append-only or not; as that
* descriptor could have come from an open() rather than an fopen(),
* that requires that it be able to do an F_GETFL fcntl() to read
* the O_ flags.
*
* Tcpdump uses ftell() to determine how much data has been written
* to a file in order to, when used with -C, determine when it's time
* to rotate capture files. ftell() therefore needs to do an lseek()
* to find out the file offset and must, thanks to the aforementioned
* optimization, also know whether the descriptor is open append-only
* or not.
*
* The net result of all the above is that we need to grant CAP_SEEK,
* CAP_WRITE, and CAP_FCNTL with the CAP_FCNTL_GETFL subcapability.
*
* Perhaps this is the universe's way of saying that either
*
* 1) there needs to be an fopenat() call and a pcap_dump_openat() call
* using it, so that Capsicum-capable tcpdump wouldn't need to do
* an fdopen()
*
* or
*
* 2) there needs to be a cap_fdopen() call in the FreeBSD standard
* I/O library that knows what rights are needed by the standard
* I/O library, based on the open mode, and assigns them, perhaps
* with an additional argument indicating, for example, whether
* seeking should be allowed, so that tcpdump doesn't need to know
* what the standard I/O library happens to require this week.
*/
static void
set_dumper_capsicum_rights(pcap_dumper_t *p)
{
int fd = fileno(pcap_dump_file(p));
cap_rights_t rights;
cap_rights_init(&rights, CAP_SEEK, CAP_WRITE, CAP_FCNTL);
if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) {
error("unable to limit dump descriptor");
}
if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) {
error("unable to limit dump descriptor fcntls");
}
}
#endif
#if MESA_DUMP
#include "mesa_net.h"
#include "stream_base.h"
static int MESA_dump_seek_to_inner(char *pkt_buf, int pktlen)
{
struct mesa_ethernet_hdr *ehdr = (struct mesa_ethernet_hdr *)pkt_buf;
char *first_ip_layer = NULL;
struct mesa_ip4_hdr *ip4hdr_greedy;
struct mesa_ip6_hdr *ip6hdr_greedy;
int bpf_match_pkt_len = -1;
int bpf_match_ipv4 = 0, bpf_match_ipv6 = 0;
if(ETHERTYPE_IP == ntohs(ehdr->ether_type)){
first_ip_layer = pkt_buf + sizeof(struct mesa_ethernet_hdr);
}else if(ETHERTYPE_IPv6 == ntohs(ehdr->ether_type)){
first_ip_layer = pkt_buf + sizeof(struct mesa_ethernet_hdr);
}else{
first_ip_layer = NULL;
}
ip4hdr_greedy = (struct mesa_ip4_hdr *)MESA_net_jump_to_layer_greedy(pkt_buf, ADDR_TYPE_MAC, __ADDR_TYPE_IP_PAIR_V4);
if(ip4hdr_greedy){
if((char *)ip4hdr_greedy == first_ip_layer){
bpf_match_pkt_len = pktlen; /* 最内层和第一层IP一样, 说明是非常标准的ethernet->IPv4包, 且无隧道, 无需memmove操作 */
}else{
if(pktlen - ((char *)ip4hdr_greedy - pkt_buf) > 0)
{
memmove(pkt_buf + sizeof(struct mesa_ethernet_hdr),
ip4hdr_greedy,
pktlen - ((char *)ip4hdr_greedy - pkt_buf));
bpf_match_pkt_len = pktlen - ((char *)ip4hdr_greedy - pkt_buf) + sizeof(struct mesa_ethernet_hdr);
ehdr->ether_type = htons(ETHERTYPE_IP); /* 第一层可能不是IPV4, 比如MPLS, VLAN等, 需要改成IP, 以便bpf过滤器能正确执行 */
}
}
if(bpf_match_pkt_len <= 0){
return -1;
}
/* 如果有正确的过滤条件, 不设采样率, 保证捕包尽量全, 符合调用者意图;
如果没有过滤条件, 即全捕包模式, 为了尽量不影响包处理线程, 根据采样率只捕一部分包.
*/
bpf_match_ipv4 = 1;
}else{
bpf_match_ipv4 = 0;
}
ip6hdr_greedy = (struct mesa_ip6_hdr *)MESA_net_jump_to_layer_greedy(pkt_buf, ADDR_TYPE_MAC, __ADDR_TYPE_IP_PAIR_V6);
if(ip6hdr_greedy){
if((char *)ip6hdr_greedy == first_ip_layer){
bpf_match_pkt_len = pktlen; /* 最内层和第一层IP一样, 说明是非常标准的ethernet->IPv6包, 且无隧道, 无需memmove操作 */
}else{
if(pktlen - ((char *)ip6hdr_greedy - pkt_buf) > 0)
{
memmove(pkt_buf + sizeof(struct mesa_ethernet_hdr),
ip6hdr_greedy,
pktlen - ((char *)ip6hdr_greedy - pkt_buf));
bpf_match_pkt_len = pktlen - ((char *)ip6hdr_greedy - pkt_buf) + sizeof(struct mesa_ethernet_hdr);
ehdr->ether_type = htons(ETHERTYPE_IPv6); /* 第一层可能不是IPV6, 比如MPLS, VLAN等,需要改成IP,以便bpf过滤器能正确执行 */
}
}
if(bpf_match_pkt_len <= 0){
///sapp_runtime_log(20, "cycle_pkt_dump_seek_to_inner_ip() length error!\n");
return -1;
}
/* 如果有正确的过滤条件, 不设采样率, 保证捕包尽量全, 符合调用者意图;
如果没有过滤条件, 即全捕包模式, 为了尽量不影响包处理线程, 根据采样率只捕一部分包.
*/
bpf_match_ipv6 = 1;
}else{
bpf_match_ipv6 = 0;
}
if(bpf_match_ipv4 || bpf_match_ipv6){
return bpf_match_pkt_len; /* 任意头部命中即可输出 */
}
return -1;
}
/* 可支持多个线程, 用逗号分隔"1,3,5,7" */
static int MESA_dump_thread_index_convert(const char *raw_index_str)
{
char *index_str = strdup(raw_index_str);
const char *delim = ",";
char *save_ptr, *section;
int index = 0;
if(NULL == memchr(index_str, ',', strlen(raw_index_str))){ /* 无逗号分隔, 仅有一个, 无多线程 */
tcpdump_thread_index_array[0] = atoi(raw_index_str);
if(tcpdump_thread_index_array[0] >= 64){
goto err;
}
tcpdump_thread_index_array_num = 1;
return 0;
}
section = strtok_r(index_str, delim, &save_ptr);
if(section){
tcpdump_thread_index_array[index] = atoi(section);
if(tcpdump_thread_index_array[index] >= 64){
goto err;
}
index++;
}
while((section = strtok_r(NULL, delim, &save_ptr))){
tcpdump_thread_index_array[index] = atoi(section);
if(tcpdump_thread_index_array[index] >= 64){
goto err;
}
index++;
}
tcpdump_thread_index_array_num = index;
free(index_str);
return 0;
err:
free(index_str);
return -1;
}
static int pkt_dump_recv_ack(int connfd)
{
char send_buf[128];
struct pkt_dump_handshake *pkt_hdr;
struct pkt_dump_opt *ack_opt;
void *ptr = &send_buf[0];
int need_len = sizeof(struct pkt_dump_handshake)+sizeof(struct pkt_dump_opt);
int ret;
printf("Wait for server ACK, if another tcpdump_mesa is running, maybe wait for a long time......\n");
while(need_len > 0){
ret = read(connfd, ptr, need_len);
if(ret <= 0){
return -1;
}
need_len -= ret;
ptr += ret;
}
pkt_hdr = (struct pkt_dump_handshake *)&send_buf[0];
ack_opt = (struct pkt_dump_opt *)(send_buf + sizeof(struct pkt_dump_handshake));
if(pkt_hdr->magic != htonl(PKT_DUMP_HDR_MAGIC)){
printf("recv ack magic error!\n");
return -1;
}
if(pkt_hdr->version != htonl(20180119)){
printf("recv ack version error!\n");
return -1;
}
if(pkt_hdr->opt_num != htonl(1)){
printf("recv ack opt_num error!\n");
return -1;
}
if(ack_opt->opt_type != htons(PKT_DUMP_OPT_ACK)){
printf("recv ack opt_type error!\n");
return -1;
}
printf("Recv server ACK success, starting packet dump.....\n");
return 0;
}
#include <pthread.h>
/*
此线程用于监测sapp的控制连接是否存活, 如果sapp退出了,
tcpdump_mesa也应该退出.
*/
static void *detect_sapp_alive_thread(void *arg)
{
int tcp_cmd_fd = (int)(long)arg;
int ret;
char nouse_buf[1500];
while(1){
ret = read(tcp_cmd_fd, nouse_buf, 1500);
if(0 == ret){
printf("\033[33m[INFO]sapp is not running, tcpdump_mesa exit!\033[0m\n");
exit(1);
}
}
return NULL;
}
static int MESA_dump_start(unsigned short udp_rcv_port, unsigned short sapp_cmd_port, char *filter)
{
int tcp_cmd_fd = -1;
int ret;
unsigned short filter_len = 0;
struct sockaddr_in sockadd;
struct pkt_dump_handshake pkt_hdr;
unsigned int opt_num = 1; /* 本端接收端口为必选项 */
struct pkt_dump_opt opt;
pthread_t pid;
tcp_cmd_fd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&sockadd, sizeof(sockadd));
sockadd.sin_family = AF_INET;
sockadd.sin_addr.s_addr = htonl(0x7f000001);
sockadd.sin_port = htons(sapp_cmd_port);
ret = connect(tcp_cmd_fd, (const struct sockaddr *)&sockadd, sizeof(sockadd));
if(ret < 0){
printf("tcpdump-mesa connect error, %s\n", strerror(errno));
return -1;
}
if(filter != NULL){
opt_num++;
}
if(tcpdump_data_offset != 0){
opt_num++;
}
if(tcpdump_thread_index_array_num > 0){
opt_num++;
}
if(tcpdump_perceptive_flag != 0){
if(NULL == filter){
printf("In perceptive mode must assign packet filter rule!\n");
exit(1);
}
opt_num++;
}
if(greedy_seek_flag != 0){
if(tcpdump_data_offset != 0){
printf("option -o and -g is exclusive, can't use at same time!\n");
exit(1);
}
opt_num++;
}
if(pkt_classify_flag != 0)
{
opt_num++;
}
if(pkt_classify_watermark_sw != 0)
{
opt_num++;
}
/************** pkt handshake *************/
pkt_hdr.magic = htonl(PKT_DUMP_HDR_MAGIC);
pkt_hdr.version = htonl(20180119); /* 之前sapp对20180119版本做了严格校验, 此处向后兼容, 先固定用此值, 以后更新sapp后, 不再校验版本 */
pkt_hdr.opt_num = htonl(opt_num);
ret = write(tcp_cmd_fd, &pkt_hdr, sizeof(pkt_hdr));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
/************** recv port *************/
opt.opt_type = htons(PKT_DUMP_OPT_RCV_PORT);
opt.opt_len = htons(sizeof(short));
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
udp_rcv_port = htons(udp_rcv_port);
ret = write(tcp_cmd_fd, &udp_rcv_port, sizeof(short));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
/************** BPF filter *************/
if(filter != NULL){
filter_len = strlen(filter) + 1; /* add EOF */
opt.opt_type = htons(PKT_DUMP_OPT_BPF_FILTER);
opt.opt_len = htons(filter_len);
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
ret = write(tcp_cmd_fd, filter, filter_len);
if(ret < 0){
printf("connection down!\n");
exit(1);
}
}
/************** data offset *************/
if(tcpdump_data_offset != 0){
unsigned short t = tcpdump_data_offset;
opt.opt_type = htons(PKT_DUMP_OPT_DATA_OFFSET);
opt.opt_len = htons(sizeof(short));
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
t = htons(t);
ret = write(tcp_cmd_fd, &t, sizeof(short));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
}
/************ thread index ************/
if(tcpdump_thread_index_array_num > 0){
opt.opt_type = htons(PKT_DUMP_OPT_THREAD_INDEX);
opt.opt_len = htons(sizeof(char) * tcpdump_thread_index_array_num);
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
ret = write(tcp_cmd_fd, tcpdump_thread_index_array, sizeof(char) * tcpdump_thread_index_array_num);
if(ret < 0){
printf("connection down!\n");
exit(1);
}
}
if(tcpdump_perceptive_flag != 0){
opt.opt_type = htons(PKT_DUMP_OPT_PERCEPTIVE);
opt.opt_len = 0;
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
}
/************** greedy seek *************/
if(greedy_seek_flag != 0){
opt.opt_type = htons(PKT_DUMP_OPT_GREEDY_SEEK);
opt.opt_len = 0;
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
}
/************** pkt classify *************/
if(pkt_classify_flag != 0){
short t = pkt_classify_flag;
opt.opt_type = htons(PKT_DUMP_OPT_CLASSIFY);
opt.opt_len = htons(sizeof(short));
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if (ret < 0)
{
printf("connection down!\n");
exit(1);
}
t = htons(t);
ret = write(tcp_cmd_fd, &t, sizeof(short));
if (ret < 0)
{
printf("connection down!\n");
exit(1);
}
}
/************** pkt classify watermark*************/
if(pkt_classify_watermark_sw != 0){
opt.opt_type = htons(PKT_DUMP_OPT_CLASSIFY_WATERMARK);
opt.opt_len = 0;
ret = write(tcp_cmd_fd, &opt, sizeof(opt));
if(ret < 0){
printf("connection down!\n");
exit(1);
}
}
/********** after send opt, start recv sapp ACK *******/
if(pkt_dump_recv_ack(tcp_cmd_fd) < 0){
printf("connection down!\n");
exit(1);
}
pthread_create(&pid, NULL, detect_sapp_alive_thread, (void *)(long)tcp_cmd_fd);
return tcp_cmd_fd;
}
static int actual_rcv_pkt_num = 0;
static void pkt_dump_signal_cb(int signo)
{
if((SIGTERM == signo) || (SIGINT == signo)){
sync();
exit(0);
}else{
;/* do nothing */
}
return;
}
/* 虚假丢包显示告警信息包 */
static const char _perceptive_pkt_data[] =
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* DMAC */
0x00, 0x00, 0x4C, 0x4F, 0x53, 0x54, /* SMAC */
0x08, 0x00, /* type */
0x45, 0x00, /* ...c..E. */
0x00, 0x41, 0xff, 0xff, 0x00, 0x00, 0xff, 0x06, /* .A...... */
0xbb, 0xb7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* ........ */
0xff, 0xff, 0x00, 0x50, 0x00, 0x50, 0x00, 0x00, /* ...P.P.. */
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x50, 0x18, /* ......P. */
0x00, 0x01, 0x3e, 0x81, 0x00, 0x00, 0x48, 0x54, /* ..>...HT */
0x54, 0x50, 0x2f, 0x31, 0x2e, 0x31, 0x20, 0x34, /* TP/1.1 4 */
0x30, 0x34, 0x20, 0x4c, 0x6f, 0x73, 0x73, 0x20, /* 04 Loss */
0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x0a /* Packet. */
};
static struct pcap_pkthdr perceptive_pcap_hdr;
static void _build_perceptive_pkt(pcap_handler callback, u_char *pcap_userdata, int loss_pkt_num)
{
#define PKT_PERCEPTIVE_STRING "HTTP/1.1 404 Loss Packet"
int i;
for(i = 0; i < loss_pkt_num; i++){
perceptive_pcap_hdr.len = 79;
perceptive_pcap_hdr.caplen = 79;
gettimeofday(&perceptive_pcap_hdr.ts, NULL);
callback(pcap_userdata, &perceptive_pcap_hdr, _perceptive_pkt_data); /* 刷屏模式调用print_packet(); 捕包模式调用: dump_packet() */
}
}
/*
从sapp捕包, 而非标准tcpdump从网卡捕包.
*/
static void MESA_dump(pcap_handler callback, u_char *pcap_userdata, char *filter,
int tot_pkt, unsigned short sapp_cmd_port )
{
unsigned short udp_default_port = 12345;
int opt, pkt_len, inner_pkt_len;
unsigned char pkt_buf[65536];
struct pcap_pkthdr phony_pcap_hdr;
int udp_rcv_fd = -1;
int tcp_cmd_fd = -1;
struct sockaddr_in sockadd;
const struct perceptive_info *pperceptive;
unsigned int cur_pkt_seq;
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, pkt_dump_signal_cb);
signal(SIGTERM, pkt_dump_signal_cb);
if(NULL == filter){
printf("\033[33m[Warning]tcpdump_mesa without BPF filter, maybe cause packet loss! So, capture in sampling mode!\033[0m\n");
}
bzero(&sockadd, sizeof(sockadd));
sockadd.sin_family = AF_INET;
sockadd.sin_addr.s_addr = htonl(INADDR_ANY);
sockadd.sin_port = htons(udp_default_port);
udp_rcv_fd = socket(AF_INET, SOCK_DGRAM, 0);
/* UDP不能开启SO_REUSEADDR, 否则多个进程能同时监听一个端口.
opt = 1;
setsockopt(udp_rcv_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));
*/
/* 从udp_default_port开始, 选择后续第一个可用端口, 防止因端口被其他应用占用而无法启动 */
while(bind(udp_rcv_fd, (struct sockaddr *) &sockadd, sizeof(sockadd)) < 0){
usleep(1000);
udp_default_port++;
sockadd.sin_port = htons(udp_default_port);
}
tcp_cmd_fd = MESA_dump_start(udp_default_port, sapp_cmd_port, filter);
if(tcp_cmd_fd < 0){
goto done;
}
pperceptive = (const struct perceptive_info *)&pkt_buf[6]; /* 存于源mac地址 */
while((-1 == tot_pkt) || (actual_rcv_pkt_num < tot_pkt)){
pkt_len = recv(udp_rcv_fd, pkt_buf, 65536, 0);
if(pkt_len > 0){
if(tcpdump_perceptive_flag > 0){
cur_pkt_seq = ntohl(pperceptive->pkt_seq);
if(perceptive_pkt_seq[pperceptive->thread_id] + 1 != cur_pkt_seq){
_build_perceptive_pkt(callback, pcap_userdata, cur_pkt_seq - perceptive_pkt_seq[pperceptive->thread_id] - 1);
}
perceptive_pkt_seq[pperceptive->thread_id] = cur_pkt_seq;
}
/* 如果有-g参数, 且写了-w, 即需要保存原始包到文件, 则不进行seek操作,
只是在没有-w 参数时, 让tcpdump能打印出包的信息, 才进行seek操作.
*/
#if 0
if((greedy_seek_flag != 0) && (dump_to_file_flag == 0)){
inner_pkt_len = MESA_dump_seek_to_inner(pkt_buf, pkt_len);
if(inner_pkt_len < 0){
continue;
}
phony_pcap_hdr.caplen = inner_pkt_len;
phony_pcap_hdr.len = inner_pkt_len;
}else{
phony_pcap_hdr.caplen = pkt_len;
phony_pcap_hdr.len = pkt_len;
}
#else
phony_pcap_hdr.caplen = pkt_len;
phony_pcap_hdr.len = pkt_len;
#endif
gettimeofday(&phony_pcap_hdr.ts, NULL);
callback(pcap_userdata, &phony_pcap_hdr, pkt_buf); /* NOTE: 刷屏模式调用print_packet(); 捕包模式调用: dump_packet() */
actual_rcv_pkt_num++;
}
}
done:
if(tcp_cmd_fd > 0){
close(tcp_cmd_fd);
}
if(udp_rcv_fd > 0){
close(udp_rcv_fd);
}
exit(1);
return;
}
static short get_pkt_classify_optarg(const char *optarg)
{
char *p_arg = strdup(optarg);
short pkt_classify_flag = 0;
char *section, *save_ptr;
section = strtok_r(p_arg, "|", &save_ptr);
if(section == NULL)
{
section = p_arg;
}
do {
if (strcasecmp(section, "in") == 0)
pkt_classify_flag |= PKT_CLASSIFY_IN;
else if (strcasecmp(section, "forward") == 0)
pkt_classify_flag |= PKT_CLASSIFY_FORWARD;
else if (strcasecmp(section, "inject") == 0)
pkt_classify_flag |= PKT_CLASSIFY_INJECT;
else if (strcasecmp(section, "drop") == 0)
pkt_classify_flag |= PKT_CLASSIFY_DROP;
else if (strcasecmp(section, "error") == 0)
pkt_classify_flag |= PKT_CLASSIFY_ERROR;
else if (strcasecmp(section, "repeat") == 0)
pkt_classify_flag |= PKT_CLASSIFY_REPEAT;
else if (strcasecmp(section, "bypass") == 0)
pkt_classify_flag |= PKT_CLASSIFY_BYPASS;
else
{
return 0;
}
}
while((section=strtok_r(NULL, "|", &save_ptr)));
free(p_arg);
return pkt_classify_flag;
}
#endif
static struct bpf_program fcode; /* lijia modify, 做为全局变量, 其他函数中调用 */
int
main(int argc, char **argv)
{
register int cnt, op, i;
bpf_u_int32 localnet =0 , netmask = 0;
int timezone_offset = 0;
register char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName;
pcap_handler callback;
int dlt;
const char *dlt_name;
#ifndef _WIN32
RETSIGTYPE (*oldhandler)(int);
#endif
struct dump_info dumpinfo;
u_char *pcap_userdata;
char ebuf[PCAP_ERRBUF_SIZE];
char VFileLine[PATH_MAX + 1];
char *username = NULL;
char *chroot_dir = NULL;
char *ret = NULL;
char *end;
#ifdef HAVE_PCAP_FINDALLDEVS
pcap_if_t *dev, *devlist;
int devnum;
#endif
int status;
FILE *VFile;
#ifdef HAVE_CAPSICUM
cap_rights_t rights;
int cansandbox;
#endif /* HAVE_CAPSICUM */
int Bflag = 0; /* buffer size */
int jflag = -1; /* packet time stamp source */
int Oflag = 1; /* run filter code optimizer */
int pflag = 0; /* don't go promiscuous */
int yflag_dlt = -1;
const char *yflag_dlt_name = NULL;
netdissect_options Ndo;
netdissect_options *ndo = &Ndo;
int immediate_mode = 0;
memset(ndo, 0, sizeof(*ndo));
ndo_set_function_pointers(ndo);
ndo->ndo_snaplen = DEFAULT_SNAPLEN;
cnt = -1;
device = NULL;
infile = NULL;
RFileName = NULL;
VFileName = NULL;
VFile = NULL;
WFileName = NULL;
dlt = -1;
#if MESA_DUMP
unsigned short sapp_cmd_port = 12345;
for(i = 0; i < 64; i++){
tcpdump_thread_index_array[i] = 255;
}
#endif
if ((cp = strrchr(argv[0], '/')) != NULL)
ndo->program_name = program_name = cp + 1;
else
ndo->program_name = program_name = argv[0];
#ifdef _WIN32
if (pcap_wsockinit() != 0)
error("Attempting to initialize Winsock failed");
#endif /* _WIN32 */
/*
* On platforms where the CPU doesn't support unaligned loads,
* force unaligned accesses to abort with SIGBUS, rather than
* being fixed up (slowly) by the OS kernel; on those platforms,
* misaligned accesses are bugs, and we want tcpdump to crash so
* that the bugs are reported.
*/
if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0)
error("%s", ebuf);
#ifdef USE_LIBSMI
smiInit("tcpdump");
#endif
while (
(op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1)
switch (op) {
case 'a':
/* compatibility for old -a */
#if MESA_DUMP
/* liji add, for perceptive, 让tcpdump_mesa能感知丢包的情况,
使用此参数后, 必须指定过滤条件,
sapp接收到此命令选项后, 先将原始包copy到临时缓冲区,
然后将每个线程的包统计计数记录到源MAC地址中发送过来,
tcpdump_mesa依次检查每个线程的计数, 如果不连续, 说明中间丢包了,
为了让捕包者能看到这个情况,
每丢一个包, 凭空造一个虚假数据包出来,
这样在wireshark上可以看到某个阶段是否有丢包, 丢了几个包, 当然看不到丢的是什么.
*/
tcpdump_perceptive_flag = 1;
#endif
break;
case 'A':
++ndo->ndo_Aflag;
break;
case 'b':
++ndo->ndo_bflag;
break;
#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
case 'B':
Bflag = atoi(optarg)*1024;
if (Bflag <= 0)
error("invalid packet buffer size %s", optarg);
break;
#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
case 'c':
cnt = atoi(optarg);
if (cnt <= 0)
error("invalid packet count %s", optarg);
break;
case 'C':
Cflag = atoi(optarg) * 1000000;
if (Cflag <= 0)
error("invalid file size %s", optarg);
break;
case 'd':
++dflag;
break;
case 'D':
Dflag++;
break;
case 'e':
++ndo->ndo_eflag;
break;
case 'E':
#ifndef HAVE_LIBCRYPTO
warning("crypto code not compiled in");
#endif
ndo->ndo_espsecret = optarg;
break;
case 'f':
++ndo->ndo_fflag;
break;
case 'F':
infile = optarg;
break;
#if MESA_DUMP
case 'g':
greedy_seek_flag = 1;
break;
#endif
case 'G':
Gflag = atoi(optarg);
if (Gflag < 0)
error("invalid number of seconds %s", optarg);
/* We will create one file initially. */
Gflag_count = 0;
/* Grab the current time for rotation use. */
if ((Gflag_time = time(NULL)) == (time_t)-1) {
error("main: can't get current time: %s",
pcap_strerror(errno));
}
break;
case 'h':
print_usage();
exit(0);
break;
case 'H':
++ndo->ndo_Hflag;
break;
case 'i':
if (optarg[0] == '0' && optarg[1] == 0)
error("Invalid adapter index");
#ifdef HAVE_PCAP_FINDALLDEVS
/*
* If the argument is a number, treat it as
* an index into the list of adapters, as
* printed by "tcpdump -D".
*
* This should be OK on UNIX systems, as interfaces
* shouldn't have names that begin with digits.
* It can be useful on Windows, where more than
* one interface can have the same name.
*/
devnum = strtol(optarg, &end, 10);
if (optarg != end && *end == '\0') {
if (devnum < 0)
error("Invalid adapter index");
if (pcap_findalldevs(&devlist, ebuf) < 0)
error("%s", ebuf);
/*
* Look for the devnum-th entry in the
* list of devices (1-based).
*/
for (i = 0, dev = devlist;
i < devnum-1 && dev != NULL;
i++, dev = dev->next)
;
if (dev == NULL)
error("Invalid adapter index");
device = strdup(dev->name);
pcap_freealldevs(devlist);
break;
}
#endif /* HAVE_PCAP_FINDALLDEVS */
device = optarg;
#if MESA_DUMP
has_device_flag = 1;
#endif
break;
#ifdef HAVE_PCAP_CREATE
case 'I':
++Iflag;
break;
#endif /* HAVE_PCAP_CREATE */
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
case 'j':
jflag = pcap_tstamp_type_name_to_val(optarg);
if (jflag < 0)
error("invalid time stamp type %s", optarg);
break;
case 'J':
Jflag++;
break;
#endif
#if MESA_DUMP
case 'k':
tcpdump_thread_index_str = optarg;
if(MESA_dump_thread_index_convert(tcpdump_thread_index_str) < 0){
printf("thread index invalid: %s\n", optarg);
exit(1);
}
break;
#endif
case 'K':
++ndo->ndo_Kflag;
break;
case 'l':
#ifdef _WIN32
/*
* _IOLBF is the same as _IOFBF in Microsoft's C
* libraries; the only alternative they offer
* is _IONBF.
*
* XXX - this should really be checking for MSVC++,
* not _WIN32, if, for example, MinGW has its own
* C library that is more UNIX-compatible.
*/
setvbuf(stdout, NULL, _IONBF, 0);
#else /* _WIN32 */
#ifdef HAVE_SETLINEBUF
setlinebuf(stdout);
#else
setvbuf(stdout, NULL, _IOLBF, 0);
#endif
#endif /* _WIN32 */
break;
case 'L':
Lflag++;
break;
case 'm':
#ifdef USE_LIBSMI
if (smiLoadModule(optarg) == 0) {
error("could not load MIB module %s", optarg);
}
ndo->ndo_mflag = 1;
#else
(void)fprintf(stderr, "%s: ignoring option `-m %s' ",
program_name, optarg);
(void)fprintf(stderr, "(no libsmi support)\n");
#endif
break;
case 'M':
/* TCP-MD5 shared secret */
#ifndef HAVE_LIBCRYPTO
warning("crypto code not compiled in");
#endif
ndo->ndo_sigsecret = optarg;
break;
case 'n':
++ndo->ndo_nflag;
break;
case 'N':
++ndo->ndo_Nflag;
break;
#if MESA_DUMP
case 'o': /* vxlan偏移量, 跳过中间某些字节 */
tcpdump_data_offset = atoi(optarg);
if(tcpdump_data_offset < 0 || tcpdump_data_offset > 1514){
printf("args [-o offset] is invalid: %s\n", optarg);
tcpdump_data_offset = 0;
}
break;
#endif
case 'O':
Oflag = 0;
break;
case 'p':
++pflag;
break;
#if MESA_DUMP
case 'P': /* sapp命令接收端口 */
{
int tmp_int_val = atoi(optarg);
if((tmp_int_val <= 0) || (tmp_int_val > 65535)){
printf("port %s invalid!\n", optarg);
exit(0);
}
sapp_cmd_port = (unsigned short)tmp_int_val;
}
break;
#endif
case 'q':
++ndo->ndo_qflag;
++ndo->ndo_suppress_default_print;
break;
#ifdef HAVE_PCAP_SETDIRECTION
case 'Q':
if (ascii_strcasecmp(optarg, "in") == 0)
Qflag = PCAP_D_IN;
else if (ascii_strcasecmp(optarg, "out") == 0)
Qflag = PCAP_D_OUT;
else if (ascii_strcasecmp(optarg, "inout") == 0)
Qflag = PCAP_D_INOUT;
else
error("unknown capture direction `%s'", optarg);
break;
#endif /* HAVE_PCAP_SETDIRECTION */
case 'r':
RFileName = optarg;
#if MESA_DUMP
has_device_flag = 1;
tcpdump_r_offline_mode = 1;
#endif
break;
case 's':
ndo->ndo_snaplen = strtol(optarg, &end, 0);
if (optarg == end || *end != '\0'
|| ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN)
error("invalid snaplen %s", optarg);
else if (ndo->ndo_snaplen == 0)
ndo->ndo_snaplen = MAXIMUM_SNAPLEN;
break;
case 'S':
++ndo->ndo_Sflag;
break;
case 't':
++ndo->ndo_tflag;
break;
case 'T':
if (ascii_strcasecmp(optarg, "vat") == 0)
ndo->ndo_packettype = PT_VAT;
else if (ascii_strcasecmp(optarg, "wb") == 0)
ndo->ndo_packettype = PT_WB;
else if (ascii_strcasecmp(optarg, "rpc") == 0)
ndo->ndo_packettype = PT_RPC;
else if (ascii_strcasecmp(optarg, "rtp") == 0)
ndo->ndo_packettype = PT_RTP;
else if (ascii_strcasecmp(optarg, "rtcp") == 0)
ndo->ndo_packettype = PT_RTCP;
else if (ascii_strcasecmp(optarg, "snmp") == 0)
ndo->ndo_packettype = PT_SNMP;
else if (ascii_strcasecmp(optarg, "cnfp") == 0)
ndo->ndo_packettype = PT_CNFP;
else if (ascii_strcasecmp(optarg, "tftp") == 0)
ndo->ndo_packettype = PT_TFTP;
else if (ascii_strcasecmp(optarg, "aodv") == 0)
ndo->ndo_packettype = PT_AODV;
else if (ascii_strcasecmp(optarg, "carp") == 0)
ndo->ndo_packettype = PT_CARP;
else if (ascii_strcasecmp(optarg, "radius") == 0)
ndo->ndo_packettype = PT_RADIUS;
else if (ascii_strcasecmp(optarg, "zmtp1") == 0)
ndo->ndo_packettype = PT_ZMTP1;
else if (ascii_strcasecmp(optarg, "vxlan") == 0)
ndo->ndo_packettype = PT_VXLAN;
else if (ascii_strcasecmp(optarg, "pgm") == 0)
ndo->ndo_packettype = PT_PGM;
else if (ascii_strcasecmp(optarg, "pgm_zmtp1") == 0)
ndo->ndo_packettype = PT_PGM_ZMTP1;
else if (ascii_strcasecmp(optarg, "lmp") == 0)
ndo->ndo_packettype = PT_LMP;
else if (ascii_strcasecmp(optarg, "resp") == 0)
ndo->ndo_packettype = PT_RESP;
else
error("unknown packet type `%s'", optarg);
break;
case 'u':
++ndo->ndo_uflag;
break;
#ifdef HAVE_PCAP_DUMP_FLUSH
case 'U':
++Uflag;
break;
#endif
case 'v':
++ndo->ndo_vflag;
break;
case 'V':
VFileName = optarg;
break;
case 'w':
WFileName = optarg;
#if MESA_DUMP
dump_to_file_flag = 1;
#endif
break;
case 'W':
Wflag = atoi(optarg);
if (Wflag <= 0)
error("invalid number of output files %s", optarg);
WflagChars = getWflagChars(Wflag);
break;
case 'x':
++ndo->ndo_xflag;
++ndo->ndo_suppress_default_print;
break;
case 'X':
++ndo->ndo_Xflag;
++ndo->ndo_suppress_default_print;
break;
case 'y':
yflag_dlt_name = optarg;
yflag_dlt =
pcap_datalink_name_to_val(yflag_dlt_name);
if (yflag_dlt < 0)
error("invalid data link type %s", yflag_dlt_name);
break;
#ifdef HAVE_PCAP_SET_PARSER_DEBUG
case 'Y':
{
/* Undocumented flag */
pcap_set_parser_debug(1);
}
break;
#endif
case 'z':
zflag = optarg;
break;
case 'Z':
username = optarg;
break;
case '#':
ndo->ndo_packet_number = 1;
break;
case OPTION_VERSION:
print_version();
exit(0);
break;
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
case OPTION_TSTAMP_PRECISION:
ndo->ndo_tstamp_precision = tstamp_precision_from_string(optarg);
if (ndo->ndo_tstamp_precision < 0)
error("unsupported time stamp precision");
break;
#endif
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
case OPTION_IMMEDIATE_MODE:
immediate_mode = 1;
break;
#endif
#if MESA_DUMP
case OPTION_VLAN_AS_MAC_IN_MAC:
treat_vlan_as_mac_in_mac_sw = 1;
break;
case OPTION_PKT_CLASSIFY:
pkt_classify_flag = get_pkt_classify_optarg(optarg);
if(pkt_classify_flag == 0)
{
error("unknown classify `%s', must be in|forward|inject|drop|error|repeat", optarg);
}
break;
case OPTION_PKT_CLASSIFY_WATERMARK:
pkt_classify_watermark_sw = 1;
break;
#endif
default:
print_usage();
exit(1);
/* NOTREACHED */
}
/**************************** cmd line parse end *************************************/
#if MESA_DUMP
device = "lo"; /* tcpdump_mesa不用指定网卡名, 默认lo */
#endif
#ifdef HAVE_PCAP_FINDALLDEVS
if (Dflag)
show_devices_and_exit();
#endif
switch (ndo->ndo_tflag) {
case 0: /* Default */
case 4: /* Default + Date*/
timezone_offset = gmt2local(0);
break;
case 1: /* No time stamp */
case 2: /* Unix timeval style */
case 3: /* Microseconds since previous packet */
case 5: /* Microseconds since first packet */
break;
default: /* Not supported */
error("only -t, -tt, -ttt, -tttt and -ttttt are supported");
break;
}
if (ndo->ndo_fflag != 0 && (VFileName != NULL || RFileName != NULL))
error("-f can not be used with -V or -r");
if (VFileName != NULL && RFileName != NULL)
error("-V and -r are mutually exclusive.");
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
/*
* If we're printing dissected packets to the standard output
* rather than saving raw packets to a file, and the standard
* output is a terminal, use immediate mode, as the user's
* probably expecting to see packets pop up immediately.
*/
if (WFileName == NULL && isatty(1))
immediate_mode = 1;
#endif
#ifdef WITH_CHROOT
/* if run as root, prepare for chrooting */
if (getuid() == 0 || geteuid() == 0) {
/* future extensibility for cmd-line arguments */
if (!chroot_dir)
chroot_dir = WITH_CHROOT;
}
#endif
#ifdef WITH_USER
/* if run as root, prepare for dropping root privileges */
if (getuid() == 0 || geteuid() == 0) {
/* Run with '-Z root' to restore old behaviour */
if (!username)
username = WITH_USER;
}
#endif
if (RFileName != NULL || VFileName != NULL) {
/*
* If RFileName is non-null, it's the pathname of a
* savefile to read. If VFileName is non-null, it's
* the pathname of a file containing a list of pathnames
* (one per line) of savefiles to read.
*
* In either case, we're reading a savefile, not doing
* a live capture.
*/
#ifndef _WIN32
/*
* We don't need network access, so relinquish any set-UID
* or set-GID privileges we have (if any).
*
* We do *not* want set-UID privileges when opening a
* trace file, as that might let the user read other
* people's trace files (especially if we're set-UID
* root).
*/
if (setgid(getgid()) != 0 || setuid(getuid()) != 0 )
fprintf(stderr, "Warning: setgid/setuid failed !\n");
#endif /* _WIN32 */
if (VFileName != NULL) {
if (VFileName[0] == '-' && VFileName[1] == '\0')
VFile = stdin;
else
VFile = fopen(VFileName, "r");
if (VFile == NULL)
error("Unable to open file: %s\n", pcap_strerror(errno));
ret = get_next_file(VFile, VFileLine);
if (!ret)
error("Nothing in %s\n", VFileName);
RFileName = VFileLine;
}
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
pd = pcap_open_offline_with_tstamp_precision(RFileName,
ndo->ndo_tstamp_precision, ebuf);
#else
pd = pcap_open_offline(RFileName, ebuf);
#endif
if (pd == NULL)
error("%s", ebuf);
#ifdef HAVE_CAPSICUM
cap_rights_init(&rights, CAP_READ);
if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 &&
errno != ENOSYS) {
error("unable to limit pcap descriptor");
}
#endif
dlt = pcap_datalink(pd);
dlt_name = pcap_datalink_val_to_name(dlt);
if (dlt_name == NULL) {
fprintf(stderr, "reading from file %s, link-type %u\n",
RFileName, dlt);
} else {
fprintf(stderr,
"reading from file %s, link-type %s (%s)\n",
RFileName, dlt_name,
pcap_datalink_val_to_description(dlt));
}
} else {
/*
*********************** We're doing a live capture. **************************
*/
if (device == NULL) {
#ifdef HAVE_PCAP_FINDALLDEVS
if (pcap_findalldevs(&devlist, ebuf) >= 0 &&
devlist != NULL) {
device = strdup(devlist->name);
pcap_freealldevs(devlist);
}
#else /* HAVE_PCAP_FINDALLDEVS */
device = pcap_lookupdev(ebuf);
#endif
if (device == NULL)
error("%s", ebuf);
}
#ifdef _WIN32
/*
* Print a message to the standard error on Windows.
* XXX - why do it here, with a different message?
*/
if(strlen(device) == 1) /* we assume that an ASCII string is always longer than 1 char */
{ /* a Unicode string has a \0 as second byte (so strlen() is 1) */
fprintf(stderr, "%s: listening on %ws\n", program_name, device);
}
else
{
fprintf(stderr, "%s: listening on %s\n", program_name, device);
}
fflush(stderr);
#endif /* _WIN32 */
#ifdef HAVE_PCAP_CREATE
pd = pcap_create(device, ebuf);
if (pd == NULL)
error("%s", ebuf);
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
if (Jflag)
show_tstamp_types_and_exit(device);
#endif
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
status = pcap_set_tstamp_precision(pd, ndo->ndo_tstamp_precision);
if (status != 0)
error("%s: Can't set %ssecond time stamp precision: %s",
device,
tstamp_precision_to_string(ndo->ndo_tstamp_precision),
pcap_statustostr(status));
#endif
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
if (immediate_mode) {
status = pcap_set_immediate_mode(pd, 1);
if (status != 0)
error("%s: Can't set immediate mode: %s",
device,
pcap_statustostr(status));
}
#endif
/*
* Is this an interface that supports monitor mode?
*/
if (pcap_can_set_rfmon(pd) == 1)
supports_monitor_mode = 1;
else
supports_monitor_mode = 0;
status = pcap_set_snaplen(pd, ndo->ndo_snaplen);
if (status != 0)
error("%s: Can't set snapshot length: %s",
device, pcap_statustostr(status));
status = pcap_set_promisc(pd, !pflag);
if (status != 0)
error("%s: Can't set promiscuous mode: %s",
device, pcap_statustostr(status));
if (Iflag) {
status = pcap_set_rfmon(pd, 1);
if (status != 0)
error("%s: Can't set monitor mode: %s",
device, pcap_statustostr(status));
}
status = pcap_set_timeout(pd, 1000);
if (status != 0)
error("%s: pcap_set_timeout failed: %s",
device, pcap_statustostr(status));
if (Bflag != 0) {
status = pcap_set_buffer_size(pd, Bflag);
if (status != 0)
error("%s: Can't set buffer size: %s",
device, pcap_statustostr(status));
}
#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
if (jflag != -1) {
status = pcap_set_tstamp_type(pd, jflag);
if (status < 0)
error("%s: Can't set time stamp type: %s",
device, pcap_statustostr(status));
}
#endif
status = pcap_activate(pd);
if (status < 0) {
/*
* pcap_activate() failed.
*/
cp = pcap_geterr(pd);
if (status == PCAP_ERROR)
error("%s", cp);
else if ((status == PCAP_ERROR_NO_SUCH_DEVICE ||
status == PCAP_ERROR_PERM_DENIED) &&
*cp != '\0')
error("%s: %s\n(%s)", device,
pcap_statustostr(status), cp);
else
error("%s: %s", device,
pcap_statustostr(status));
} else if (status > 0) {
/*
* pcap_activate() succeeded, but it's warning us
* of a problem it had.
*/
cp = pcap_geterr(pd);
if (status == PCAP_WARNING)
warning("%s", cp);
else if (status == PCAP_WARNING_PROMISC_NOTSUP &&
*cp != '\0')
warning("%s: %s\n(%s)", device,
pcap_statustostr(status), cp);
else
warning("%s: %s", device,
pcap_statustostr(status));
}
#ifdef HAVE_PCAP_SETDIRECTION
if (Qflag != -1) {
status = pcap_setdirection(pd, Qflag);
if (status != 0)
error("%s: pcap_setdirection() failed: %s",
device, pcap_geterr(pd));
}
#endif /* HAVE_PCAP_SETDIRECTION */
#else
*ebuf = '\0';
pd = pcap_open_live(device, ndo->ndo_snaplen, !pflag, 1000,
ebuf);
if (pd == NULL)
error("%s", ebuf);
else if (*ebuf)
warning("%s", ebuf);
#endif /* HAVE_PCAP_CREATE */
/*
* Let user own process after socket has been opened.
*/
#ifndef _WIN32
if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
fprintf(stderr, "Warning: setgid/setuid failed !\n");
#endif /* _WIN32 */
#if !defined(HAVE_PCAP_CREATE) && defined(_WIN32)
if(Bflag != 0)
if(pcap_setbuff(pd, Bflag)==-1){
error("%s", pcap_geterr(pd));
}
#endif /* !defined(HAVE_PCAP_CREATE) && defined(_WIN32) */
if (Lflag)
show_dlts_and_exit(device);
if (yflag_dlt >= 0) {
#ifdef HAVE_PCAP_SET_DATALINK
if (pcap_set_datalink(pd, yflag_dlt) < 0)
error("%s", pcap_geterr(pd));
#else
/*
* We don't actually support changing the
* data link type, so we only let them
* set it to what it already is.
*/
if (yflag_dlt != pcap_datalink(pd)) {
error("%s is not one of the DLTs supported by this device\n",
yflag_dlt_name);
}
#endif
(void)fprintf(stderr, "%s: data link type %s\n",
program_name, yflag_dlt_name);
(void)fflush(stderr);
}
i = pcap_snapshot(pd);
if (ndo->ndo_snaplen < i) {
warning("snaplen raised from %d to %d", ndo->ndo_snaplen, i);
ndo->ndo_snaplen = i;
}
if(ndo->ndo_fflag != 0) {
if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
warning("foreign (-f) flag used but: %s", ebuf);
}
}
}
if (infile)
cmdbuf = read_infile(infile);
else
cmdbuf = copy_argv(&argv[optind]);
#ifdef HAVE_PCAP_SET_OPTIMIZER_DEBUG
pcap_set_optimizer_debug(dflag);
#endif
if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0){
error("%s", pcap_geterr(pd));
}else{
/* 不一定有bpf filter, 此处判断一下 */
if(cmdbuf){
has_bpf_filter_flag = 1;
}
}
if (dflag) {
bpf_dump(&fcode, dflag);
pcap_close(pd);
free(cmdbuf);
pcap_freecode(&fcode);
exit(0);
}
init_print(ndo, localnet, netmask, timezone_offset);
#ifndef _WIN32
(void)setsignal(SIGPIPE, cleanup);
(void)setsignal(SIGTERM, cleanup);
(void)setsignal(SIGINT, cleanup);
#endif /* _WIN32 */
#if defined(HAVE_FORK) || defined(HAVE_VFORK)
(void)setsignal(SIGCHLD, child_cleanup);
#endif
/* Cooperate with nohup(1) */
#ifndef _WIN32
if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL)
(void)setsignal(SIGHUP, oldhandler);
#endif /* _WIN32 */
#ifndef _WIN32
/*
* If a user name was specified with "-Z", attempt to switch to
* that user's UID. This would probably be used with sudo,
* to allow tcpdump to be run in a special restricted
* account (if you just want to allow users to open capture
* devices, and can't just give users that permission,
* you'd make tcpdump set-UID or set-GID).
*
* Tcpdump doesn't necessarily write only to one savefile;
* the general only way to allow a -Z instance to write to
* savefiles as the user under whose UID it's run, rather
* than as the user specified with -Z, would thus be to switch
* to the original user ID before opening a capture file and
* then switch back to the -Z user ID after opening the savefile.
* Switching to the -Z user ID only after opening the first
* savefile doesn't handle the general case.
*/
if (getuid() == 0 || geteuid() == 0) {
#ifdef HAVE_LIBCAP_NG
/* Initialize capng */
capng_clear(CAPNG_SELECT_BOTH);
if (username) {
capng_updatev(
CAPNG_ADD,
CAPNG_PERMITTED | CAPNG_EFFECTIVE,
CAP_SETUID,
CAP_SETGID,
-1);
}
if (WFileName) {
capng_update(
CAPNG_ADD,
CAPNG_PERMITTED | CAPNG_EFFECTIVE,
CAP_DAC_OVERRIDE
);
}
capng_apply(CAPNG_SELECT_BOTH);
#endif /* HAVE_LIBCAP_NG */
if (username || chroot_dir)
droproot(username, chroot_dir);
}
#endif /* _WIN32 */
#if MESA_DUMP
/*
如果使用了 -g参数, 表示用最内层的IP,PORT做为过滤条件, 不能直接将bpf应用到pcap句柄,
因为那还是用最外层过滤, 如果是隧道, 一个包也过滤不到.
此处不能加过滤条件, 而是在收到包后, 主动调用bpf_filter()再检测一遍,
比直接在pcap底层应用bpf效率有点低.
*/
if(0 == greedy_seek_flag){
if (pcap_setfilter(pd, &fcode) < 0)
error("%s", pcap_geterr(pd));
}
else
{
pcap_freecode(&fcode);
if(pcap_compile_nopcap(Oflag, DLT_RAW, &fcode, cmdbuf, 0, netmask) < 0){
printf("Compile pcap filter %s error\n", cmdbuf);
return -1;
}
}
#else
if (pcap_setfilter(pd, &fcode) < 0)
error("%s", pcap_geterr(pd));
#endif
#ifdef HAVE_CAPSICUM
if (RFileName == NULL && VFileName == NULL) {
static const unsigned long cmds[] = { BIOCGSTATS, BIOCROTZBUF };
cap_rights_init(&rights, CAP_IOCTL, CAP_READ);
if (cap_rights_limit(pcap_fileno(pd), &rights) < 0 &&
errno != ENOSYS) {
error("unable to limit pcap descriptor");
}
if (cap_ioctls_limit(pcap_fileno(pd), cmds,
sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) {
error("unable to limit ioctls on pcap descriptor");
}
}
#endif
if (WFileName) {
pcap_dumper_t *p;
/* Do not exceed the default PATH_MAX for files. */
dumpinfo.CurrentFileName = (char *)malloc(PATH_MAX + 1);
if (dumpinfo.CurrentFileName == NULL)
error("malloc of dumpinfo.CurrentFileName");
/* We do not need numbering for dumpfiles if Cflag isn't set. */
if (Cflag != 0)
MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, WflagChars);
else
MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0);
p = pcap_dump_open(pd, dumpinfo.CurrentFileName);
#ifdef HAVE_LIBCAP_NG
/* Give up CAP_DAC_OVERRIDE capability.
* Only allow it to be restored if the -C or -G flag have been
* set since we may need to create more files later on.
*/
capng_update(
CAPNG_DROP,
(Cflag || Gflag ? 0 : CAPNG_PERMITTED)
| CAPNG_EFFECTIVE,
CAP_DAC_OVERRIDE
);
capng_apply(CAPNG_SELECT_BOTH);
#endif /* HAVE_LIBCAP_NG */
if (p == NULL)
error("%s", pcap_geterr(pd));
#ifdef HAVE_CAPSICUM
set_dumper_capsicum_rights(p);
#endif
if (Cflag != 0 || Gflag != 0) {
#ifdef HAVE_CAPSICUM
dumpinfo.WFileName = strdup(basename(WFileName));
if (dumpinfo.WFileName == NULL) {
error("Unable to allocate memory for file %s",
WFileName);
}
dumpinfo.dirfd = open(dirname(WFileName),
O_DIRECTORY | O_RDONLY);
if (dumpinfo.dirfd < 0) {
error("unable to open directory %s",
dirname(WFileName));
}
cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL,
CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE);
if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 &&
errno != ENOSYS) {
error("unable to limit directory rights");
}
if (cap_fcntls_limit(dumpinfo.dirfd, CAP_FCNTL_GETFL) < 0 &&
errno != ENOSYS) {
error("unable to limit dump descriptor fcntls");
}
#else /* !HAVE_CAPSICUM */
dumpinfo.WFileName = WFileName;
#endif
callback = dump_packet_and_trunc;
dumpinfo.pd = pd;
dumpinfo.p = p;
pcap_userdata = (u_char *)&dumpinfo;
} else {
callback = dump_packet;
#if MESA_DUMP
/* 如果设定了greedy选项且有BPF规则, 需要调用MESA_dump_packet(), 偏移到内层IP再保存 */
if((greedy_seek_flag != 0) && (has_bpf_filter_flag != 0)){
callback = MESA_dump_packet; /* 更新callback指针 */
}
#endif
pcap_userdata = (u_char *)p;
}
#ifdef HAVE_PCAP_DUMP_FLUSH
if (Uflag)
pcap_dump_flush(p);
#endif
} else {
dlt = pcap_datalink(pd);
ndo->ndo_if_printer = get_if_printer(ndo, dlt);
callback = print_packet;
#if MESA_DUMP
/* 如果设定了greedy选项, 需要调用MESA_dump_print_packet(), 偏移到内层IP再处理 */
if(greedy_seek_flag != 0){
callback = MESA_dump_print_packet; /* 更新callback指针 */
}
#endif
pcap_userdata = (u_char *)ndo;
}
#ifdef SIGNAL_REQ_INFO
/*
* We can't get statistics when reading from a file rather
* than capturing from a device.
*/
if (RFileName == NULL)
(void)setsignal(SIGNAL_REQ_INFO, requestinfo);
#endif
if (ndo->ndo_vflag > 0 && WFileName) {
/*
* When capturing to a file, "-v" means tcpdump should,
* every 10 seconds, "v"erbosely report the number of
* packets captured.
*/
#ifdef USE_WIN32_MM_TIMER
/* call verbose_stats_dump() each 1000 +/-100msec */
timer_id = timeSetEvent(1000, 100, verbose_stats_dump, 0, TIME_PERIODIC);
setvbuf(stderr, NULL, _IONBF, 0);
#elif defined(HAVE_ALARM)
(void)setsignal(SIGALRM, verbose_stats_dump);
alarm(1);
#endif
}
#ifndef _WIN32
if (RFileName == NULL) {
/*
* Live capture (if -V was specified, we set RFileName
* to a file from the -V file). Print a message to
* the standard error on UN*X.
*/
if (!ndo->ndo_vflag && !WFileName) {
(void)fprintf(stderr,
"%s: verbose output suppressed, use -v or -vv for full protocol decode\n",
program_name);
} else
(void)fprintf(stderr, "%s: ", program_name);
dlt = pcap_datalink(pd);
dlt_name = pcap_datalink_val_to_name(dlt);
if (dlt_name == NULL) {
(void)fprintf(stderr, "listening on %s, link-type %u, capture size %u bytes\n",
device, dlt, ndo->ndo_snaplen);
} else {
(void)fprintf(stderr, "listening on %s, link-type %s (%s), capture size %u bytes\n",
device, dlt_name,
pcap_datalink_val_to_description(dlt), ndo->ndo_snaplen);
}
(void)fflush(stderr);
}
#endif /* _WIN32 */
#ifdef HAVE_CAPSICUM
cansandbox = (ndo->ndo_nflag && VFileName == NULL && zflag == NULL);
if (cansandbox && cap_enter() < 0 && errno != ENOSYS)
error("unable to enter the capability mode");
#endif /* HAVE_CAPSICUM */
/***************************** starting capture... ***********************************/
if(tcpdump_data_offset != 0 && greedy_seek_flag != 0){
printf("option -o and -g is exclusive, can't use at same time!\n");
exit(1);
}
if(0 == has_device_flag){
MESA_dump(callback, pcap_userdata, cmdbuf, cnt, sapp_cmd_port);
}
do {
status = pcap_loop(pd, cnt, callback, pcap_userdata);
if (WFileName == NULL) {
/*
* We're printing packets. Flush the printed output,
* so it doesn't get intermingled with error output.
*/
if (status == -2) {
/*
* We got interrupted, so perhaps we didn't
* manage to finish a line we were printing.
* Print an extra newline, just in case.
*/
putchar('\n');
}
(void)fflush(stdout);
}
if (status == -2) {
/*
* We got interrupted. If we are reading multiple
* files (via -V) set these so that we stop.
*/
VFileName = NULL;
ret = NULL;
}
if (status == -1) {
/*
* Error. Report it.
*/
(void)fprintf(stderr, "%s: pcap_loop: %s\n",
program_name, pcap_geterr(pd));
}
if (RFileName == NULL) {
/*
* We're doing a live capture. Report the capture
* statistics.
*/
info(1);
}
pcap_close(pd);
if (VFileName != NULL) {
ret = get_next_file(VFile, VFileLine);
if (ret) {
int new_dlt;
RFileName = VFileLine;
pd = pcap_open_offline(RFileName, ebuf);
if (pd == NULL)
error("%s", ebuf);
#ifdef HAVE_CAPSICUM
cap_rights_init(&rights, CAP_READ);
if (cap_rights_limit(fileno(pcap_file(pd)),
&rights) < 0 && errno != ENOSYS) {
error("unable to limit pcap descriptor");
}
#endif
new_dlt = pcap_datalink(pd);
if (new_dlt != dlt) {
/*
* The new file has a different
* link-layer header type from the
* previous one.
*/
if (WFileName != NULL) {
/*
* We're writing raw packets
* that match the filter to
* a pcap file. pcap files
* don't support multiple
* different link-layer
* header types, so we fail
* here.
*/
error("%s: new dlt does not match original", RFileName);
}
/*
* We're printing the decoded packets;
* switch to the new DLT.
*
* To do that, we need to change
* the printer, change the DLT name,
* and recompile the filter with
* the new DLT.
*/
dlt = new_dlt;
ndo->ndo_if_printer = get_if_printer(ndo, dlt);
if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
error("%s", pcap_geterr(pd));
}
/*
* Set the filter on the new file.
*/
if (pcap_setfilter(pd, &fcode) < 0)
error("%s", pcap_geterr(pd));
/*
* Report the new file.
*/
dlt_name = pcap_datalink_val_to_name(dlt);
if (dlt_name == NULL) {
fprintf(stderr, "reading from file %s, link-type %u\n",
RFileName, dlt);
} else {
fprintf(stderr,
"reading from file %s, link-type %s (%s)\n",
RFileName, dlt_name,
pcap_datalink_val_to_description(dlt));
}
}
}
}
while (ret != NULL);
free(cmdbuf);
pcap_freecode(&fcode);
exit(status == -1 ? 1 : 0);
}
/* make a clean exit on interrupts */
static RETSIGTYPE
cleanup(int signo _U_)
{
#ifdef USE_WIN32_MM_TIMER
if (timer_id)
timeKillEvent(timer_id);
timer_id = 0;
#elif defined(HAVE_ALARM)
alarm(0);
#endif
#ifdef HAVE_PCAP_BREAKLOOP
/*
* We have "pcap_breakloop()"; use it, so that we do as little
* as possible in the signal handler (it's probably not safe
* to do anything with standard I/O streams in a signal handler -
* the ANSI C standard doesn't say it is).
*/
pcap_breakloop(pd);
#if MESA_DUMP
(void)fflush(stdout);
exit(0);
#endif
#else
/*
* We don't have "pcap_breakloop()"; this isn't safe, but
* it's the best we can do. Print the summary if we're
* not reading from a savefile - i.e., if we're doing a
* live capture - and exit.
*/
if (pd != NULL && pcap_file(pd) == NULL) {
/*
* We got interrupted, so perhaps we didn't
* manage to finish a line we were printing.
* Print an extra newline, just in case.
*/
putchar('\n');
(void)fflush(stdout);
info(1);
}
exit(0);
#endif
}
/*
On windows, we do not use a fork, so we do not care less about
waiting a child processes to die
*/
#if defined(HAVE_FORK) || defined(HAVE_VFORK)
static RETSIGTYPE
child_cleanup(int signo _U_)
{
wait(NULL);
}
#endif /* HAVE_FORK && HAVE_VFORK */
static void
info(register int verbose)
{
struct pcap_stat stats;
/*
* Older versions of libpcap didn't set ps_ifdrop on some
* platforms; initialize it to 0 to handle that.
*/
stats.ps_ifdrop = 0;
if (pcap_stats(pd, &stats) < 0) {
(void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd));
infoprint = 0;
return;
}
if (!verbose)
fprintf(stderr, "%s: ", program_name);
(void)fprintf(stderr, "%u packet%s captured", packets_captured,
PLURAL_SUFFIX(packets_captured));
if (!verbose)
fputs(", ", stderr);
else
putc('\n', stderr);
(void)fprintf(stderr, "%u packet%s received by filter", stats.ps_recv,
PLURAL_SUFFIX(stats.ps_recv));
if (!verbose)
fputs(", ", stderr);
else
putc('\n', stderr);
(void)fprintf(stderr, "%u packet%s dropped by kernel", stats.ps_drop,
PLURAL_SUFFIX(stats.ps_drop));
if (stats.ps_ifdrop != 0) {
if (!verbose)
fputs(", ", stderr);
else
putc('\n', stderr);
(void)fprintf(stderr, "%u packet%s dropped by interface\n",
stats.ps_ifdrop, PLURAL_SUFFIX(stats.ps_ifdrop));
} else
putc('\n', stderr);
infoprint = 0;
}
#if defined(HAVE_FORK) || defined(HAVE_VFORK)
#ifdef HAVE_FORK
#define fork_subprocess() fork()
#else
#define fork_subprocess() vfork()
#endif
static void
compress_savefile(const char *filename)
{
pid_t child;
child = fork_subprocess();
if (child == -1) {
fprintf(stderr,
"compress_savefile: fork failed: %s\n",
pcap_strerror(errno));
return;
}
if (child != 0) {
/* Parent process. */
return;
}
/*
* Child process.
* Set to lowest priority so that this doesn't disturb the capture.
*/
#ifdef NZERO
setpriority(PRIO_PROCESS, 0, NZERO - 1);
#else
setpriority(PRIO_PROCESS, 0, 19);
#endif
if (execlp(zflag, zflag, filename, (char *)NULL) == -1)
fprintf(stderr,
"compress_savefile: execlp(%s, %s) failed: %s\n",
zflag,
filename,
pcap_strerror(errno));
#ifdef HAVE_FORK
exit(1);
#else
_exit(1);
#endif
}
#else /* HAVE_FORK && HAVE_VFORK */
static void
compress_savefile(const char *filename)
{
fprintf(stderr,
"compress_savefile failed. Functionality not implemented under your system\n");
}
#endif /* HAVE_FORK && HAVE_VFORK */
static void
dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
struct dump_info *dump_info;
++packets_captured;
++infodelay;
dump_info = (struct dump_info *)user;
/*
* XXX - this won't force the file to rotate on the specified time
* boundary, but it will rotate on the first packet received after the
* specified Gflag number of seconds. Note: if a Gflag time boundary
* and a Cflag size boundary coincide, the time rotation will occur
* first thereby cancelling the Cflag boundary (since the file should
* be 0).
*/
if (Gflag != 0) {
/* Check if it is time to rotate */
time_t t;
/* Get the current time */
if ((t = time(NULL)) == (time_t)-1) {
error("dump_and_trunc_packet: can't get current_time: %s",
pcap_strerror(errno));
}
/* If the time is greater than the specified window, rotate */
if (t - Gflag_time >= Gflag) {
#ifdef HAVE_CAPSICUM
FILE *fp;
int fd;
#endif
/* Update the Gflag_time */
Gflag_time = t;
/* Update Gflag_count */
Gflag_count++;
/*
* Close the current file and open a new one.
*/
pcap_dump_close(dump_info->p);
/*
* Compress the file we just closed, if the user asked for it
*/
if (zflag != NULL)
compress_savefile(dump_info->CurrentFileName);
/*
* Check to see if we've exceeded the Wflag (when
* not using Cflag).
*/
if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag) {
(void)fprintf(stderr, "Maximum file limit reached: %d\n",
Wflag);
exit(0);
/* NOTREACHED */
}
if (dump_info->CurrentFileName != NULL)
free(dump_info->CurrentFileName);
/* Allocate space for max filename + \0. */
dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
if (dump_info->CurrentFileName == NULL)
error("dump_packet_and_trunc: malloc");
/*
* Gflag was set otherwise we wouldn't be here. Reset the count
* so multiple files would end with 1,2,3 in the filename.
* The counting is handled with the -C flow after this.
*/
Cflag_count = 0;
/*
* This is always the first file in the Cflag
* rotation: e.g. 0
* We also don't need numbering if Cflag is not set.
*/
if (Cflag != 0)
MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0,
WflagChars);
else
MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0);
#ifdef HAVE_LIBCAP_NG
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
capng_apply(CAPNG_SELECT_BOTH);
#endif /* HAVE_LIBCAP_NG */
#ifdef HAVE_CAPSICUM
fd = openat(dump_info->dirfd,
dump_info->CurrentFileName,
O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
error("unable to open file %s",
dump_info->CurrentFileName);
}
fp = fdopen(fd, "w");
if (fp == NULL) {
error("unable to fdopen file %s",
dump_info->CurrentFileName);
}
dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
#else /* !HAVE_CAPSICUM */
dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
#endif
#ifdef HAVE_LIBCAP_NG
capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
capng_apply(CAPNG_SELECT_BOTH);
#endif /* HAVE_LIBCAP_NG */
if (dump_info->p == NULL)
error("%s", pcap_geterr(pd));
#ifdef HAVE_CAPSICUM
set_dumper_capsicum_rights(dump_info->p);
#endif
}
}
/*
* XXX - this won't prevent capture files from getting
* larger than Cflag - the last packet written to the
* file could put it over Cflag.
*/
if (Cflag != 0) {
long size = pcap_dump_ftell(dump_info->p);
if (size == -1)
error("ftell fails on output file");
if (size > Cflag) {
#ifdef HAVE_CAPSICUM
FILE *fp;
int fd;
#endif
/*
* Close the current file and open a new one.
*/
pcap_dump_close(dump_info->p);
/*
* Compress the file we just closed, if the user
* asked for it.
*/
if (zflag != NULL)
compress_savefile(dump_info->CurrentFileName);
Cflag_count++;
if (Wflag > 0) {
if (Cflag_count >= Wflag)
Cflag_count = 0;
}
if (dump_info->CurrentFileName != NULL)
free(dump_info->CurrentFileName);
dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
if (dump_info->CurrentFileName == NULL)
error("dump_packet_and_trunc: malloc");
MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
#ifdef HAVE_LIBCAP_NG
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
capng_apply(CAPNG_SELECT_BOTH);
#endif /* HAVE_LIBCAP_NG */
#ifdef HAVE_CAPSICUM
fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd < 0) {
error("unable to open file %s",
dump_info->CurrentFileName);
}
fp = fdopen(fd, "w");
if (fp == NULL) {
error("unable to fdopen file %s",
dump_info->CurrentFileName);
}
dump_info->p = pcap_dump_fopen(dump_info->pd, fp);
#else /* !HAVE_CAPSICUM */
dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
#endif
#ifdef HAVE_LIBCAP_NG
capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
capng_apply(CAPNG_SELECT_BOTH);
#endif /* HAVE_LIBCAP_NG */
if (dump_info->p == NULL)
error("%s", pcap_geterr(pd));
#ifdef HAVE_CAPSICUM
set_dumper_capsicum_rights(dump_info->p);
#endif
}
}
pcap_dump((u_char *)dump_info->p, h, sp);
#ifdef HAVE_PCAP_DUMP_FLUSH
if (Uflag)
pcap_dump_flush(dump_info->p);
#endif
--infodelay;
if (infoprint)
info(0);
}
static void
dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
++packets_captured;
++infodelay;
/*
sapp内部也处理了-o参数, 通过udp socket输出的包已经跳过了前面的offset字节,
此时再跳过offset就重复偏移两次了,
所以要判断一下tcpdump_r_offline_mode.
*/
if((tcpdump_data_offset > 0) && (tcpdump_r_offline_mode != 0))
{
pcap_dump(user, h, sp+tcpdump_data_offset);
}
else
{
pcap_dump(user, h, sp);
}
#ifdef HAVE_PCAP_DUMP_FLUSH
if (Uflag)
pcap_dump_flush((pcap_dumper_t *)user);
#endif
--infodelay;
if (infoprint)
info(0);
}
static void
MESA_dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *raw_pkt)
{
//char modify_pkt_buf[2048];
int inner_pkt_len;
++packets_captured;
++infodelay;
//memcpy(modify_pkt_buf, raw_pkt, h->caplen >= 2048? 2048:h->caplen);
//inner_pkt_len = MESA_dump_seek_to_inner(modify_pkt_buf, h->caplen);
//if(inner_pkt_len < 0){
// return;
//}
struct mesa_ip4_hdr *ip4hdr_greedy;
struct mesa_ip6_hdr *ip6hdr_greedy;
const unsigned char *inner_iphdr = NULL;
ip4hdr_greedy = (struct mesa_ip4_hdr *)MESA_net_jump_to_layer_greedy(raw_pkt, ADDR_TYPE_MAC, __ADDR_TYPE_IP_PAIR_V4);
if(ip4hdr_greedy)
{
inner_iphdr = (const unsigned char *)ip4hdr_greedy;
inner_pkt_len = h->caplen - ((const u_char *)ip4hdr_greedy - raw_pkt) ;
}
else
{
ip6hdr_greedy = (struct mesa_ip6_hdr *)MESA_net_jump_to_layer_greedy(raw_pkt, ADDR_TYPE_MAC, __ADDR_TYPE_IP_PAIR_V6);
if(ip6hdr_greedy)
{
inner_iphdr = (const unsigned char *)ip6hdr_greedy;
inner_pkt_len = h->caplen - ((const u_char *)ip6hdr_greedy - raw_pkt);
}
else
{
return;
}
}
if(has_bpf_filter_flag != 0){
if(0 == bpf_filter(fcode.bf_insns,
(const unsigned char *)inner_iphdr, inner_pkt_len, inner_pkt_len)){
return;
}
}
/* -w参数要存储包, 实际存储的包还是用原始报文, 只是BPF用内层过滤 */
pcap_dump(user, h, raw_pkt);
#ifdef HAVE_PCAP_DUMP_FLUSH
if (Uflag)
pcap_dump_flush((pcap_dumper_t *)user);
#endif
--infodelay;
if (infoprint)
info(0);
}
static void
print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
{
++packets_captured;
++infodelay;
/*
sapp内部也处理了-o参数, 通过udp socket输出的包已经跳过了前面的offset字节,
此时再跳过offset就重复偏移两次了,
所以要判断一下tcpdump_r_offline_mode.
*/
if((tcpdump_data_offset > 0) && (tcpdump_r_offline_mode != 0))
{
pretty_print_packet((netdissect_options *)user, h, sp+tcpdump_data_offset, packets_captured);
}
else
{
pretty_print_packet((netdissect_options *)user, h, sp, packets_captured);
}
--infodelay;
if (infoprint)
info(0);
}
static void
MESA_dump_print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *pkt)
{
int inner_pkt_len;
#if 0
/* 此函数仅用于tcpdump屏幕打印, 直接修改pkt原始包, 避免再copy一次, 节约点CPU */
inner_pkt_len = MESA_dump_seek_to_inner(pkt, h->caplen);
if(inner_pkt_len < 0){
return;
}
if(has_bpf_filter_flag != 0){
if(0 == bpf_filter(fcode.bf_insns,
(const unsigned char *)pkt, inner_pkt_len, inner_pkt_len)){
return;
}
}
/* 改为新的修改后的数据包长度 */
((struct pcap_pkthdr *)h)->caplen = (unsigned int)inner_pkt_len;
((struct pcap_pkthdr *)h)->len = (unsigned int)inner_pkt_len;
#else
struct mesa_ip4_hdr *ip4hdr_greedy;
struct mesa_ip6_hdr *ip6hdr_greedy;
const unsigned char *inner_iphdr = NULL;
ip4hdr_greedy = (struct mesa_ip4_hdr *)MESA_net_jump_to_layer_greedy(pkt, ADDR_TYPE_MAC, __ADDR_TYPE_IP_PAIR_V4);
if(ip4hdr_greedy)
{
inner_iphdr = (const unsigned char *)ip4hdr_greedy;
inner_pkt_len = h->caplen - ((const unsigned char *)ip4hdr_greedy - pkt);
}
else
{
ip6hdr_greedy = (struct mesa_ip6_hdr *)MESA_net_jump_to_layer_greedy(pkt, ADDR_TYPE_MAC, __ADDR_TYPE_IP_PAIR_V6);
if(ip6hdr_greedy)
{
inner_iphdr = (const unsigned char *)ip6hdr_greedy;
inner_pkt_len = h->caplen - ((const unsigned char *)ip6hdr_greedy - pkt);
}
else
{
return;
}
}
if (has_bpf_filter_flag != 0)
{
if (0 == bpf_filter(fcode.bf_insns,
(const unsigned char *)inner_iphdr, inner_pkt_len, inner_pkt_len))
{
return;
}
}
#endif
print_packet(user, h, pkt);
}
#ifdef _WIN32
/*
* XXX - there should really be libpcap calls to get the version
* number as a string (the string would be generated from #defines
* at run time, so that it's not generated from string constants
* in the library, as, on many UNIX systems, those constants would
* be statically linked into the application executable image, and
* would thus reflect the version of libpcap on the system on
* which the application was *linked*, not the system on which it's
* *running*.
*
* That routine should be documented, unlike the "version[]"
* string, so that UNIX vendors providing their own libpcaps
* don't omit it (as a couple of vendors have...).
*
* Packet.dll should perhaps also export a routine to return the
* version number of the Packet.dll code, to supply the
* "Wpcap_version" information on Windows.
*/
char WDversion[]="current-git.tcpdump.org";
#if !defined(HAVE_GENERATED_VERSION)
char version[]="current-git.tcpdump.org";
#endif
char pcap_version[]="current-git.tcpdump.org";
char Wpcap_version[]="3.1";
#endif
#ifdef SIGNAL_REQ_INFO
RETSIGTYPE requestinfo(int signo _U_)
{
if (infodelay)
++infoprint;
else
info(0);
}
#endif
/*
* Called once each second in verbose mode while dumping to file
*/
#ifdef USE_WIN32_MM_TIMER
void CALLBACK verbose_stats_dump (UINT timer_id _U_, UINT msg _U_, DWORD_PTR arg _U_,
DWORD_PTR dw1 _U_, DWORD_PTR dw2 _U_)
{
if (infodelay == 0)
fprintf(stderr, "Got %u\r", packets_captured);
}
#elif defined(HAVE_ALARM)
static void verbose_stats_dump(int sig _U_)
{
if (infodelay == 0)
fprintf(stderr, "Got %u\r", packets_captured);
alarm(1);
}
#endif
USES_APPLE_DEPRECATED_API
static void
print_version(void)
{
extern char version[];
#ifndef HAVE_PCAP_LIB_VERSION
#if defined(_WIN32) || defined(HAVE_PCAP_VERSION)
extern char pcap_version[];
#else /* defined(_WIN32) || defined(HAVE_PCAP_VERSION) */
static char pcap_version[] = "unknown";
#endif /* defined(_WIN32) || defined(HAVE_PCAP_VERSION) */
#endif /* HAVE_PCAP_LIB_VERSION */
#ifdef HAVE_PCAP_LIB_VERSION
#ifdef _WIN32
(void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version);
#else /* _WIN32 */
(void)fprintf(stderr, "tcpdump_mesa version %s\n", tcpdump_mesa_version);
(void)fprintf(stderr, "tcpdump version %s\n", version);
#endif /* _WIN32 */
(void)fprintf(stderr, "%s\n",pcap_lib_version());
#else /* HAVE_PCAP_LIB_VERSION */
#ifdef _WIN32
(void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version);
(void)fprintf(stderr, "WinPcap version %s, based on libpcap version %s\n",Wpcap_version, pcap_version);
#else /* _WIN32 */
(void)fprintf(stderr, "tcpdump_mesa version %s\n", tcpdump_mesa_version);
(void)fprintf(stderr, "tcpdump version %s\n", version);
(void)fprintf(stderr, "libpcap version %s\n", pcap_version);
#endif /* _WIN32 */
#endif /* HAVE_PCAP_LIB_VERSION */
#if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION)
(void)fprintf (stderr, "%s\n", SSLeay_version(SSLEAY_VERSION));
#endif
#ifdef USE_LIBSMI
(void)fprintf (stderr, "SMI-library: %s\n", smi_version_string);
#endif
}
USES_APPLE_RST
static void
print_usage(void)
{
print_version();
(void)fprintf(stderr,
"Usage: %s [-aAbd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ]\n", program_name);
(void)fprintf(stderr,
"\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n");
(void)fprintf(stderr,
"\t\t[ -i interface ]" j_FLAG_USAGE " [ -M secret ] [ --number ]\n");
#ifdef HAVE_PCAP_SETDIRECTION
(void)fprintf(stderr,
"\t\t[ -Q in|out|inout ]\n");
#endif
(void)fprintf(stderr,
"\t\t[ -r file ] [ -s snaplen ] ");
#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
(void)fprintf(stderr, "[ --time-stamp-precision precision ]\n");
(void)fprintf(stderr,
"\t\t");
#endif
#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
(void)fprintf(stderr, "[ --immediate-mode ] ");
#endif
(void)fprintf(stderr, "[ -T type ] [ --version ] [ -V file ]\n");
(void)fprintf(stderr,
"\t\t[ -w file ] [ -W filecount ] [ -y datalinktype ] [ -z postrotate-command ]\n");
(void)fprintf(stderr,
"\t\t[ -Z user ] [ expression ]\n");
#if MESA_DUMP
(void)fprintf(stderr,
"----------------------------------------------------------------------------------------------.\n");
(void)fprintf(stderr,
"\t\tThe follow args is customized for tcpdump_mesa(%s):\n", tcpdump_mesa_version);
(void)fprintf(stderr,
"\t\t[ -a ] enable perceptive mode, can detect loss packet number.\n");
(void)fprintf(stderr,
"\t\t[ -g greedy-seek ] enable greedy seek to most inner IP layer, for tunnel, embed protocol.\n");
(void)fprintf(stderr,
"\t\t[ -k thread-id ] to assign sapp recv thread id, support multi-range, for example: 1,3,5,7.\n");
(void)fprintf(stderr,
"\t\t[ -o offset ] to assign offset from MAC, for skip some low layer data, for example: jump vxlan using -o 50, jump mac_in_mac using -o 14.\n");
(void)fprintf(stderr,
"\t\t[ -P port ] to assign sapp recv command port.\n");
(void)fprintf(stderr,
"\t\t[ --vlan-as-mac-in-mac ] force VLAN to be analysed as MAC-IN-MAC format.\n");
(void)fprintf(stderr,
"\t\t[ --classify in|forward|inject|drop|error|repeat|bypass ]. specify packet capture classifier by direction and operation\n");
(void)fprintf(stderr,
"\t\t[ --enable_classify_watermark ]. enable record classify type in src mac address\n");
#endif
}
/*
* Local Variables:
* c-style: whitesmith
* c-basic-offset: 8
* End:
*/