From e601cdb8cab0aca61280be33381c4fdc793d33cb Mon Sep 17 00:00:00 2001 From: lijia Date: Fri, 16 Nov 2018 11:42:54 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=BF=87=E6=BB=A4=E6=9C=80?= =?UTF-8?q?=E5=86=85=E5=B1=82=E6=95=B0=E6=8D=AE=E5=8C=85=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- net_common.c | 20 +++++-- tcpdump.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 154 insertions(+), 19 deletions(-) diff --git a/net_common.c b/net_common.c index 4a80624..0214216 100644 --- a/net_common.c +++ b/net_common.c @@ -27,7 +27,7 @@ static int vlan8021q_jump_to_layer(const char *raw_data, int raw_layer_type, in static int ipv4_jump_to_layer(const char *raw_data, int raw_layer_type, int expect_layer_type); static int ipv6_jump_to_layer(const char *raw_data, int raw_layer_type, int expect_layer_type); -static int treat_vlan_as_mac_in_mac_sw = 1; +int treat_vlan_as_mac_in_mac_sw = 0; /* 将vlan强制按mac_in_mac格式解析, 先不传递给sapp, 因为影响sapp全局处理开关 */ /* ascii字符转16进制 */ char MESA_ascii_to_hex(char ascii) @@ -124,10 +124,19 @@ static int udp_jump_to_layer(const char *raw_data, int raw_layer_type, int expe if((2152 == usport) && (2152 == udport)){ skip_len = gtp_jump_to_layer(raw_data+sizeof(struct mesa_udp_hdr), ADDR_TYPE_UDP, expect_layer_type); + }else if(4789 == udport){ + /* vxlan模式暂时只支持ethernet. TODO: 如果是hdlc, ppp封装, 需要实现一个单独的vxlan_jump_to_layer()函数 */ + skip_len = eth_jump_to_layer(raw_data+sizeof(struct mesa_udp_hdr)+8, ADDR_TYPE_MAC, expect_layer_type); + if(skip_len < 0){ + return -1; + } + skip_len += 8; /* skip vxlan header */ }else if((3544 == usport) || (3544 == udport)){ - ; - //TODO - //skip_len = teredo_jump_to_layer(raw_data+sizeof(struct mesa_udp_hdr), ADDR_TYPE_UDP, expect_layer_type); + /* teredo实际没有数据包头, 直接跳转到ipv6层即可 */ + skip_len = ipv6_jump_to_layer(raw_data+sizeof(struct mesa_udp_hdr), __ADDR_TYPE_IP_PAIR_V6, expect_layer_type); + if(skip_len < 0){ + return -1; + } }else{ /* 其他UDP类型不支持再跳转 */ return -1; @@ -247,6 +256,7 @@ static int ipv6_jump_to_layer(const char *raw_data, int raw_layer_type, int exp if(ADDR_TYPE_UDP == expect_layer_type){ skip_len = next_hdr_ptr - (UINT8 *)raw_data; }else{ + /* TODO: IPv6的其他隧道模式 */ skip_len = -1; } goto done; @@ -478,7 +488,7 @@ static int vlan8021q_jump_to_layer(const char *raw_data, int raw_layer_type, in break; default: - printf("TODO: jmp unsupport type in vlan8021q, 0x%x!\n", ntohs(vlan_hdr->type)); + printf("unsupport type in vlan8021q, 0x%x, You can try set the '--vlan-as-mac-in-mac' arg!\n", ntohs(vlan_hdr->type)); skip_len = -1; } diff --git a/tcpdump.c b/tcpdump.c index a890e07..06562b8 100644 --- a/tcpdump.c +++ b/tcpdump.c @@ -52,7 +52,11 @@ 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 参数 */ +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; + #endif #ifndef lint @@ -207,6 +211,10 @@ static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_ 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 @@ -517,6 +525,10 @@ show_devices_and_exit (void) #define OPTION_TSTAMP_PRECISION 129 #define OPTION_IMMEDIATE_MODE 130 +#if MESA_DUMP +#define OPTION_VLAN_AS_MAC_IN_MAC 131 /* 短参数不够用了, 增加长参数 */ +#endif + static const struct option longopts[] = { #if defined(HAVE_PCAP_CREATE) || defined(_WIN32) { "buffer-size", required_argument, NULL, 'B' }, @@ -556,6 +568,9 @@ static const struct option longopts[] = { { "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 }, +#endif { NULL, 0, NULL, 0 } }; @@ -810,7 +825,7 @@ static int MESA_dump_seek_to_inner(char *pkt_buf, int pktlen) 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操作 */ + bpf_match_pkt_len = pktlen; /* 最内层和第一层IP一样, 说明是非常标准的ethernet->IPv4包, 且无隧道, 无需memmove操作 */ }else{ memmove(pkt_buf + sizeof(struct mesa_ethernet_hdr), ip4hdr_greedy, @@ -835,7 +850,7 @@ static int MESA_dump_seek_to_inner(char *pkt_buf, int pktlen) 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操作 */ + bpf_match_pkt_len = pktlen; /* 最内层和第一层IP一样, 说明是非常标准的ethernet->IPv6包, 且无隧道, 无需memmove操作 */ }else{ memmove(pkt_buf + sizeof(struct mesa_ethernet_hdr), ip6hdr_greedy, @@ -1187,6 +1202,9 @@ static void _build_perceptive_pkt(pcap_handler callback, u_char *pcap_userdata, } } +/* + 从sapp捕包, 而非标准tcpdump从网卡捕包. +*/ static void MESA_dump(pcap_handler callback, u_char *pcap_userdata, char *filter, int tot_pkt, unsigned short sapp_cmd_port ) { @@ -1274,10 +1292,14 @@ done: close(udp_rcv_fd); } + exit(1); + return; } #endif +static struct bpf_program fcode; /* lijia modify, 做为全局变量, 其他函数中调用 */ + int main(int argc, char **argv) { @@ -1288,7 +1310,7 @@ main(int argc, char **argv) pcap_handler callback; int dlt; const char *dlt_name; - struct bpf_program fcode; + #ifndef _WIN32 RETSIGTYPE (*oldhandler)(int); #endif @@ -1511,6 +1533,9 @@ main(int argc, char **argv) } #endif /* HAVE_PCAP_FINDALLDEVS */ device = optarg; +#if MESA_DUMP + has_device_flag = 1; +#endif break; #ifdef HAVE_PCAP_CREATE @@ -1650,12 +1675,10 @@ main(int argc, char **argv) #endif /* HAVE_PCAP_SETDIRECTION */ case 'r': -#if MESA_DUMP - printf("tcpdump_mesa not support -r arg, only support capture from sapp so far, TODO!\n"); - exit(1); -#else RFileName = optarg; -#endif +#if MESA_DUMP + has_device_flag = 1; +#endif break; case 's': @@ -1803,6 +1826,12 @@ main(int argc, char **argv) break; #endif +#if MESA_DUMP + case OPTION_VLAN_AS_MAC_IN_MAC: + treat_vlan_as_mac_in_mac_sw = 1; + break; +#endif + default: print_usage(); exit(1); @@ -2145,8 +2174,12 @@ main(int argc, char **argv) #ifdef HAVE_PCAP_SET_OPTIMIZER_DEBUG pcap_set_optimizer_debug(dflag); #endif - if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) + if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0){ error("%s", pcap_geterr(pd)); + }else{ + has_bpf_filter_flag = 1; + } + if (dflag) { bpf_dump(&fcode, dflag); pcap_close(pd); @@ -2220,8 +2253,23 @@ main(int argc, char **argv) } #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 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 }; @@ -2304,6 +2352,12 @@ main(int argc, char **argv) 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 @@ -2314,6 +2368,12 @@ main(int argc, char **argv) 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; } @@ -2376,7 +2436,11 @@ main(int argc, char **argv) #endif /* HAVE_CAPSICUM */ /***************************** starting capture... ***********************************/ -#if (0 == MESA_DUMP) + + 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) { @@ -2491,9 +2555,6 @@ main(int argc, char **argv) } } while (ret != NULL); -#else - MESA_dump(callback, pcap_userdata, cmdbuf, cnt, sapp_cmd_port); -#endif free(cmdbuf); pcap_freecode(&fcode); @@ -2875,6 +2936,40 @@ dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 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; + } + if(has_bpf_filter_flag != 0){ + if(0 == bpf_filter(fcode.bf_insns, + (const unsigned char *)modify_pkt_buf, 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) { @@ -2889,6 +2984,34 @@ print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) info(0); } + +static void +MESA_dump_print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *pkt) +{ + int inner_pkt_len; + + /* 此函数仅用于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; + + print_packet(user, h, pkt); +} + + + #ifdef _WIN32 /* * XXX - there should really be libpcap calls to get the version @@ -3029,6 +3152,8 @@ print_usage(void) "\t\t[ -o offset ] to assign offset from MAC, for skip some low layer data, for example: vxlan=50, mac_in_mac=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"); #endif } /*