diff --git a/src/packet/ipv4_utils.h b/src/packet/ipv4_utils.h index 60910e4..a038b60 100644 --- a/src/packet/ipv4_utils.h +++ b/src/packet/ipv4_utils.h @@ -6,6 +6,7 @@ extern "C" { #endif +#include #include #include #include @@ -255,6 +256,25 @@ static inline void ipv4_hdr_set_opt_data(struct ip *hdr, const char *opt_data) memcpy((char *)hdr + sizeof(struct ip), opt_data, ipv4_hdr_get_opt_len(hdr)); } +static inline int ipv4_hdr_to_str(const struct ip *hdr, char *buf, size_t size) +{ + memset(buf, 0, size); + char src_addr_str[INET6_ADDRSTRLEN] = {0}; + char dst_addr_str[INET6_ADDRSTRLEN] = {0}; + + struct in_addr src_addr = ipv4_hdr_get_src_in_addr(hdr); + struct in_addr dst_addr = ipv4_hdr_get_dst_in_addr(hdr); + inet_ntop(AF_INET, &src_addr, src_addr_str, sizeof(src_addr_str)); + inet_ntop(AF_INET, &dst_addr, dst_addr_str, sizeof(dst_addr_str)); + + return snprintf(buf, size, "IPv4: version=%u hdr_len=%u tos=%u total_len=%u ipid=%u flags=%u(rf=%u df=%u mf=%u) frag_offset=%u ttl=%u proto=%u checksum=%u src_addr=%s dst_addr=%s opt_len=%u", + ipv4_hdr_get_version(hdr), ipv4_hdr_get_hdr_len(hdr), ipv4_hdr_get_tos(hdr), + ipv4_hdr_get_total_len(hdr), ipv4_hdr_get_ipid(hdr), ipv4_hdr_get_flags(hdr), + ipv4_hdr_get_rf_flag(hdr), ipv4_hdr_get_df_flag(hdr), ipv4_hdr_get_mf_flag(hdr), + ipv4_hdr_get_frag_offset(hdr), ipv4_hdr_get_ttl(hdr), ipv4_hdr_get_proto(hdr), + ipv4_hdr_get_checksum(hdr), src_addr_str, dst_addr_str, ipv4_hdr_get_opt_len(hdr)); +} + #ifdef __cplusplus } #endif diff --git a/src/packet/ipv6_utils.h b/src/packet/ipv6_utils.h index 37e2fdd..1811314 100644 --- a/src/packet/ipv6_utils.h +++ b/src/packet/ipv6_utils.h @@ -6,6 +6,7 @@ extern "C" { #endif +#include #include #include @@ -195,6 +196,22 @@ static inline void ipv6_frag_set_more(struct ip6_frag *frag, bool more) } } +static inline int ipv6_hdr_to_str(const struct ip6_hdr *hdr, char *buf, size_t size) +{ + memset(buf, 0, size); + char src_addr_str[INET6_ADDRSTRLEN] = {0}; + char dst_addr_str[INET6_ADDRSTRLEN] = {0}; + + struct in6_addr src_addr = ipv6_hdr_get_src_in6_addr(hdr); + struct in6_addr dst_addr = ipv6_hdr_get_dst_in6_addr(hdr); + inet_ntop(AF_INET6, &src_addr, src_addr_str, INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &dst_addr, dst_addr_str, INET6_ADDRSTRLEN); + + return snprintf(buf, size, "IPv6: version=%u traffic_class=%u flow_label=%u payload_len=%u next_header=%u hop_limit=%u src_addr=%s dst_addr=%s", + ipv6_hdr_get_version(hdr), ipv6_hdr_get_traffic_class(hdr), ipv6_hdr_get_flow_label(hdr), ipv6_hdr_get_payload_len(hdr), + ipv6_hdr_get_next_header(hdr), ipv6_hdr_get_hop_limit(hdr), src_addr_str, dst_addr_str); +} + #ifdef __cplusplus } #endif diff --git a/src/packet/packet.cpp b/src/packet/packet.cpp index 514b420..261ae4a 100644 --- a/src/packet/packet.cpp +++ b/src/packet/packet.cpp @@ -1021,9 +1021,9 @@ static inline const char *parse_ipv4(struct packet *pkt, const char *data, uint1 // ip fragmented if (ipv4_hdr_get_mf_flag(hdr) || ipv4_hdr_get_frag_offset(hdr)) { - PACKET_LOG_WARN("ip is fragmented"); + PACKET_LOG_WARN("packet %p ip layer %p is fragmented", pkt, layer); pkt->frag_layer = layer; - return layer->pld_ptr; + // try continue parse } // TESTED @@ -1057,9 +1057,9 @@ static inline const char *parse_ipv6(struct packet *pkt, const char *data, uint1 // ipv6 fragment if (next_proto == IPPROTO_FRAGMENT) { - PACKET_LOG_WARN("ipv6 is fragmented"); + PACKET_LOG_WARN("packet %p ipv6 layer %p is fragmented", pkt, layer); pkt->frag_layer = layer; - return layer->pld_ptr; + // try continue parse } // TODO parse ipv6 extension headers @@ -1363,21 +1363,63 @@ void packet_print_str(const struct packet *pkt) return; } + char buffer[2048] = {0}; printf("packet: %p, data_ptr: %p, data_len: %u, layers_used: %u, layers_size: %u\n", pkt, pkt->data_ptr, pkt->data_len, pkt->layers_used, pkt->layers_size); for (uint8_t i = 0; i < pkt->layers_used; i++) { + int used = 0; const struct packet_layer *layer = &pkt->layers[i]; printf(" layer[%u]: %p, type: %s, hdr_offset: %u, hdr_ptr: %p, hdr_len: %u, pld_ptr: %p, pld_len: %u\n", i, layer, layer_type_to_str(layer->type), layer->hdr_offset, layer->hdr_ptr, layer->hdr_len, layer->pld_ptr, layer->pld_len); + switch (layer->type) + { + case LAYER_TYPE_ETHER: + break; + case LAYER_TYPE_PPP: + break; + case LAYER_TYPE_HDLC: + break; + case LAYER_TYPE_L2TP: + break; + case LAYER_TYPE_VLAN: + break; + case LAYER_TYPE_PPPOE: + break; + case LAYER_TYPE_MPLS: + break; + case LAYER_TYPE_IPV4: + used = ipv4_hdr_to_str((const struct ip *)layer->hdr_ptr, buffer, sizeof(buffer)); + break; + case LAYER_TYPE_IPV6: + used = ipv6_hdr_to_str((const struct ip6_hdr *)layer->hdr_ptr, buffer, sizeof(buffer)); + break; + case LAYER_TYPE_GRE: + break; + case LAYER_TYPE_UDP: + used = udp_hdr_to_str((const struct udphdr *)layer->hdr_ptr, buffer, sizeof(buffer)); + break; + case LAYER_TYPE_TCP: + used = tcp_hdr_to_str((const struct tcphdr *)layer->hdr_ptr, buffer, sizeof(buffer)); + break; + case LAYER_TYPE_ICMP: + break; + case LAYER_TYPE_ICMP6: + break; + case LAYER_TYPE_VXLAN: + break; + case LAYER_TYPE_GTPV1_U: + break; + default: + break; + } + if (used) + { + printf(" %s\n", buffer); + } } - for (uint16_t i = 0; i < pkt->data_len; i++) - { - printf("0x%02x, ", (uint8_t)pkt->data_ptr[i]); - } - printf("\n"); } void packet_print_table(const struct packet *pkt) diff --git a/src/packet/tcp_utils.h b/src/packet/tcp_utils.h index 25b9d3a..90440dd 100644 --- a/src/packet/tcp_utils.h +++ b/src/packet/tcp_utils.h @@ -6,6 +6,7 @@ extern "C" { #endif +#include #include #include #define __FAVOR_BSD 1 @@ -261,6 +262,23 @@ static inline void tcp_hdr_set_opt_data(struct tcphdr *hdr, const char *ptr) memcpy((char *)hdr + sizeof(struct tcphdr), ptr, tcp_hdr_get_opt_len(hdr)); } +static inline int tcp_hdr_to_str(const struct tcphdr *hdr, char *buf, size_t size) +{ + memset(buf, 0, size); + return snprintf(buf, size, "TCP: src_port=%u dst_port=%u seq=%u ack=%u hdr_len=%u flags=0x%02x(%s%s%s%s%s%s) window=%u checksum=%u urg_ptr=%u opt_len=%u", + tcp_hdr_get_src_port(hdr), tcp_hdr_get_dst_port(hdr), + tcp_hdr_get_seq(hdr), tcp_hdr_get_ack(hdr), + tcp_hdr_get_hdr_len(hdr), tcp_hdr_get_flags(hdr), + tcp_hdr_get_urg_flag(hdr) ? "URG " : "", + tcp_hdr_get_ack_flag(hdr) ? "ACK " : "", + tcp_hdr_get_push_flag(hdr) ? "PSH " : "", + tcp_hdr_get_rst_flag(hdr) ? "RST " : "", + tcp_hdr_get_syn_flag(hdr) ? "SYN " : "", + tcp_hdr_get_fin_flag(hdr) ? "FIN " : "", + tcp_hdr_get_window(hdr), tcp_hdr_get_checksum(hdr), + tcp_hdr_get_urg_ptr(hdr), tcp_hdr_get_opt_len(hdr)); +} + #ifdef __cplusplus } #endif diff --git a/src/packet/udp_utils.h b/src/packet/udp_utils.h index 54023e3..1142ce8 100644 --- a/src/packet/udp_utils.h +++ b/src/packet/udp_utils.h @@ -6,6 +6,7 @@ extern "C" { #endif +#include #include #define __FAVOR_BSD 1 #include @@ -72,6 +73,14 @@ static inline void udp_hdr_set_checksum(struct udphdr *hdr, uint16_t sum) hdr->uh_sum = htons(sum); } +static inline int udp_hdr_to_str(const struct udphdr *hdr, char *buf, size_t size) +{ + memset(buf, 0, size); + return snprintf(buf, size, "UDP: src_port=%u dst_port=%u total_len=%u checksum=%u", + udp_hdr_get_src_port(hdr), udp_hdr_get_dst_port(hdr), + udp_hdr_get_total_len(hdr), udp_hdr_get_checksum(hdr)); +} + #ifdef __cplusplus } #endif diff --git a/test/packet_parser/CMakeLists.txt b/test/packet_parser/CMakeLists.txt index 65103ff..11a1c7a 100644 --- a/test/packet_parser/CMakeLists.txt +++ b/test/packet_parser/CMakeLists.txt @@ -1,3 +1,6 @@ # build packet_parser add_executable(packet_parser packet_parser.cpp) -target_link_libraries(packet_parser packet pcap) \ No newline at end of file +target_link_libraries(packet_parser packet pcap) + +file(COPY split_pcap.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +file(COPY cmp_layers.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) \ No newline at end of file diff --git a/test/packet_parser/cmp_layers.sh b/test/packet_parser/cmp_layers.sh new file mode 100644 index 0000000..791c203 --- /dev/null +++ b/test/packet_parser/cmp_layers.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +input=$1 +if [ -d "$input" ]; then + input_dir=$input + pcap_files=($(find ${input_dir} -type f -name "*.pcap")) +elif [ -f "$input" ]; then + input_file=$input + pcap_files=($input_file) +else + echo "Usage: $0 input_pcap_dir or input_pcap_file" + exit 1 +fi + +# remove l7 protocol fields +function replace() { + file=$1 + array=(":data" ":ntp" ":rip" ":isakmp" ":esp" ":udpencap" ":sip" ":sdp" ":rtcp" ":rtp" ":ssh" ":dns" ":ssl" ":gquic" ":http-text-lines" ":http" ":msmms" ":bfd" ":ftp-data-text-lines" ":ftp" ":ssdp" ":mdns" ":radius" ":pop" ":smtp" ":rtmpt" ":bittorrent" ":oicq" ":json" ":media" ":x11" ":telnet" ":nbss:smb" ":memcache" ":rtspi" ":rdt" ":rtsp" ":nbns" ":nbdgm:smb:browser" ":lcp" ":chap" ":ipcp" ":comp_data" ":ccp" ":snmp" ":socks" ":bgp" ":eigrp" ":bootp" ":xml" ":echo" ":vssmonitoring" ":mndp" ":websocket-text-lines" ":websocket" ":image-jfif" ":png" ":pkix1implicit" ":x509sat" ":x509ce" ":pkix1explicit" ":llmnr") + for key in "${array[@]}"; do + sed "s/$key//g" ${file} >.tmp.txt + mv .tmp.txt ${file} + done +} + +output_dir="cmp_output/" +for pcap in "${pcap_files[@]}"; do + rm -rf ${output_dir} && mkdir ${output_dir} + + # tshark output frame.protocols + tshark -r ${pcap} -T fields -e frame.number -e frame.protocols >>${output_dir}/tshark.txt + + # packet_parser output frame.protocols + ./packet_parser -f ${pcap} -p >>${output_dir}/parser.txt + + # compare tshark and packet_parser output + cp ${output_dir}/tshark.txt ${output_dir}/expect.txt + replace ${output_dir}/expect.txt + diff ${output_dir}/expect.txt ${output_dir}/parser.txt >>${output_dir}/diff.txt + + # print result + line_count=$(cat ${output_dir}/diff.txt | wc -l) + if [ "$line_count" -ne 0 ]; then + printf "\033[31m ${pcap} TEST FAILED \033[0m\n" + cat ${output_dir}/diff.txt | head -n 100 + #exit 0 + else + printf "\033[32m ${pcap} TEST PASSED \033[0m\n" + fi + +done diff --git a/test/packet_parser/packet_parser.cpp b/test/packet_parser/packet_parser.cpp index 95248cd..fca3958 100644 --- a/test/packet_parser/packet_parser.cpp +++ b/test/packet_parser/packet_parser.cpp @@ -2,11 +2,106 @@ #include #include "packet_priv.h" +struct options +{ + char *file; + int print_proto; + int print_summary; +}; + +static uint64_t number = 0; + +static int packet_proto_to_str(const struct packet *pkt, char *buff, int size) +{ + int used = 0; + int8_t num = packet_get_layers_number(pkt); + for (int8_t i = 0; i < num; i++) + { + const struct packet_layer *layer = packet_get_layer(pkt, i); + switch (layer->type) + { + case LAYER_TYPE_ETHER: + used += snprintf(buff + used, size - used, "eth:ethertype"); + break; + case LAYER_TYPE_PPP: + used += snprintf(buff + used, size - used, "ppp"); + break; + case LAYER_TYPE_HDLC: + used += snprintf(buff + used, size - used, "hdlc"); + break; + case LAYER_TYPE_L2TP: + used += snprintf(buff + used, size - used, "l2tp"); + break; + case LAYER_TYPE_VLAN: + used += snprintf(buff + used, size - used, "vlan:ethertype"); + break; + case LAYER_TYPE_PPPOE: + used += snprintf(buff + used, size - used, "pppoe"); + break; + case LAYER_TYPE_MPLS: + used += snprintf(buff + used, size - used, "mpls"); + break; + case LAYER_TYPE_IPV4: + used += snprintf(buff + used, size - used, "ip"); + break; + case LAYER_TYPE_IPV6: + used += snprintf(buff + used, size - used, "ipv6"); + break; + case LAYER_TYPE_GRE: + used += snprintf(buff + used, size - used, "gre"); + break; + case LAYER_TYPE_UDP: + used += snprintf(buff + used, size - used, "udp"); + break; + case LAYER_TYPE_TCP: + used += snprintf(buff + used, size - used, "tcp"); + break; + case LAYER_TYPE_ICMP: + used += snprintf(buff + used, size - used, "icmp"); + break; + case LAYER_TYPE_ICMP6: + used += snprintf(buff + used, size - used, "icmpv6"); + break; + case LAYER_TYPE_VXLAN: + used += snprintf(buff + used, size - used, "vxlan"); + break; + case LAYER_TYPE_GTPV1_U: + used += snprintf(buff + used, size - used, "gtp"); + break; + default: + used += snprintf(buff + used, size - used, "unknown"); + break; + } + + if (i != num - 1) + { + used += snprintf(buff + used, size - used, ":"); + } + } + + return used; +} + static void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) { + struct options *opts = (struct options *)user; + struct packet pkt; packet_parse(&pkt, (const char *)bytes, h->caplen); - packet_print_table(&pkt); + number++; + + if (opts->print_summary) + { + printf("\033[0;32m frame=%lu len=%u \033[0m", number, h->caplen); + packet_print_str(&pkt); + } + + if (opts->print_proto) + { + char buff[1024] = {0}; + packet_proto_to_str(&pkt, buff, sizeof(buff)); + printf("%lu %s\n", number, buff); + } } static void usage(char *cmd) @@ -14,6 +109,8 @@ static void usage(char *cmd) printf("Usage: %s\n", cmd); printf("Options:\n"); printf(" -f pcap file\n"); + printf(" -p print protocol\n"); + printf(" -s print summary\n"); printf(" -h print help\n"); printf("\n"); } @@ -21,36 +118,40 @@ static void usage(char *cmd) int main(int argc, char **argv) { int opt = 0; - char *file = NULL; - while ((opt = getopt(argc, argv, "f:h")) != -1) + struct options opts = {0}; + while ((opt = getopt(argc, argv, "f:psh")) != -1) { switch (opt) { case 'f': - file = optarg; + opts.file = optarg; + break; + case 'p': + opts.print_proto = 1; + break; + case 's': + opts.print_summary = 1; break; case 'h': - usage(argv[0]); - return 0; default: usage(argv[0]); - return -1; + return 0; } } - if (file == NULL) + if (opts.file == NULL) { usage(argv[0]); return -1; } - pcap_t *pcap = pcap_open_offline(file, NULL); + pcap_t *pcap = pcap_open_offline(opts.file, NULL); if (pcap == NULL) { printf("pcap_open_offline() failed\n"); return -1; } - pcap_loop(pcap, -1, packet_handler, NULL); + pcap_loop(pcap, -1, packet_handler, (u_char *)&opts); pcap_close(pcap); return 0; diff --git a/test/packet_parser/split_pcap.sh b/test/packet_parser/split_pcap.sh new file mode 100644 index 0000000..a2cf7da --- /dev/null +++ b/test/packet_parser/split_pcap.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Usage: $0 input.pcap" + exit 1 +fi + +input_pcap="$1" +output_dir="split_output/" +rm -rf $output_dir && mkdir -p $output_dir + +num=$(tshark -r "$input_pcap" -T fields -e frame.number | tail -n 1) +echo -e "\e[32m input pcap $input_pcap has $num packets\e[0m" + +for ((i = 1; i <= num; i++)); do + tshark -r "$input_pcap" -Y "frame.number==$i" -w "$output_dir/packet_$i.pcap" +done + +echo -e "\e[32m split pcap is saved in $output_dir\e[0m"