diff --git a/include/stellar/layer.h b/include/stellar/layer.h index 0ec9699..4cf6acb 100644 --- a/include/stellar/layer.h +++ b/include/stellar/layer.h @@ -79,12 +79,15 @@ int packet_get_layer_by_idx(const struct packet *pkt, int idx, struct layer *out for (int i = packet_get_layer_count(pkt) - 1; i >= 0 && packet_get_layer_by_idx(pkt, i, &layer) == 0; i--) #define PACKET_GETALL_LAYERS(pkt, layers) \ - { \ - int num = MIN(packet_get_layer_count(pkt), (sizeof(layers) / sizeof(layers[0]))); \ + ({ \ + memset(layers, 0, sizeof(layers)); \ + int size = sizeof(layers) / sizeof(layers[0]); \ + int count = packet_get_layer_count(pkt); \ + int num = count > size ? size : count; \ for (int i = 0; i < num && packet_get_layer_by_idx(pkt, i, &layers[i]) == 0; i++) \ /* void */; \ - return num; \ - } + num; \ + }) #ifdef __cplusplus } diff --git a/include/stellar/packet.h b/include/stellar/packet.h index 8995986..9562dfa 100644 --- a/include/stellar/packet.h +++ b/include/stellar/packet.h @@ -38,6 +38,7 @@ uint16_t packet_get_payload_len(const struct packet *pkt); struct packet *imitate_tcp_packet(const struct packet *origin_pkt, uint32_t tcp_seq, uint32_t tcp_ack, uint8_t tcp_flags, const char *tcp_payload, uint16_t tcp_payload_len); struct packet *imitate_udp_packet(const struct packet *origin_pkt, const char *udp_payload, uint16_t udp_payload_len); +struct packet *craft_packet_from_scratch(const struct layer larers[], uint16_t layer_count, const char *payload, uint16_t payload_len); #ifdef __cplusplus } diff --git a/src/packet/packet_build.cpp b/src/packet/packet_build.cpp index 277e120..16dfbb3 100644 --- a/src/packet/packet_build.cpp +++ b/src/packet/packet_build.cpp @@ -253,3 +253,120 @@ struct packet *imitate_udp_packet(const struct packet *origin_pkt, const char *u return new_pkt; } + +struct packet *craft_packet_from_scratch(const struct layer layers[], uint16_t layer_count, const char *payload, uint16_t payload_len) +{ + // check arguments + if (layers == NULL || layer_count == 0 || (payload == NULL && payload_len != 0) || (payload != NULL && payload_len == 0)) + { + PACKET_BUILD_LOG_ERROR("craft packet from scratch failed, invalid arguments"); + return NULL; + } + + // calculate the new packet length + uint16_t new_pkt_len = 0; + for (int i = 0; i < layer_count; i++) + { + if (layers[i].hdr.raw == NULL || layers[i].hdr_len == 0) + { + PACKET_BUILD_LOG_ERROR("craft packet from scratch failed, the header of layer %d is invalid", i); + return NULL; + } + new_pkt_len += layers[i].hdr_len; + } + new_pkt_len += payload_len; + struct packet *new_pkt = packet_new(new_pkt_len); + if (new_pkt == NULL) + { + PACKET_BUILD_LOG_ERROR("craft packet from scratch failed, no space to allocate new packet"); + return NULL; + } + + // copy the data to the new packet + char *new_pkt_data = (char *)packet_get_raw_data(new_pkt); + int offset = 0; + for (int i = 0; i < layer_count; i++) + { + memcpy(new_pkt_data + offset, layers[i].hdr.raw, layers[i].hdr_len); + offset += layers[i].hdr_len; + } + memcpy(new_pkt_data + offset, payload, payload_len); + + // update the headers of the new packet + struct tcphdr *tcp_hdr = NULL; + struct udphdr *udp_hdr = NULL; + struct ip *ip4_hdr = NULL; + struct ip6_hdr *ip6_hdr = NULL; + // update checksums and lengths + uint16_t curr_layer_payload_len = payload_len; + for (int i = layer_count - 1; i >= 0; i--) + { + switch (layers[i].proto) + { + case LAYER_PROTO_TCP: + tcp_hdr = (struct tcphdr *)(new_pkt_data + new_pkt_len - layers[i].hdr_len - curr_layer_payload_len); + // update the TCP header + tcp_hdr_set_hdr_len(tcp_hdr, layers[i].hdr_len); + tcp_hdr_set_checksum(tcp_hdr, 0); + curr_layer_payload_len += layers[i].hdr_len; + break; + case LAYER_PROTO_UDP: + udp_hdr = (struct udphdr *)(new_pkt_data + new_pkt_len - layers[i].hdr_len - curr_layer_payload_len); + // update the UDP header + udp_hdr_set_total_len(udp_hdr, layers[i].hdr_len + curr_layer_payload_len); + udp_hdr_set_checksum(udp_hdr, 0); + curr_layer_payload_len += layers[i].hdr_len; + break; + case LAYER_PROTO_IPV4: + ip4_hdr = (struct ip *)(new_pkt_data + new_pkt_len - layers[i].hdr_len - curr_layer_payload_len); + // update the checksums of the upper layer + if (i + 1 < layer_count && layers[i + 1].proto == LAYER_PROTO_TCP) + { + tcp_hdr = (struct tcphdr *)(new_pkt_data + new_pkt_len - curr_layer_payload_len); + tcp_hdr->th_sum = checksum_v4(tcp_hdr, curr_layer_payload_len, IPPROTO_TCP, &ip4_hdr->ip_src, &ip4_hdr->ip_dst); + } + if (i + 1 < layer_count && layers[i + 1].proto == LAYER_PROTO_UDP) + { + udp_hdr = (struct udphdr *)(new_pkt_data + new_pkt_len - curr_layer_payload_len); + udp_hdr->uh_sum = checksum_v4(udp_hdr, curr_layer_payload_len, IPPROTO_UDP, &ip4_hdr->ip_src, &ip4_hdr->ip_dst); + } + // update the IPv4 header + ipv4_hdr_set_hdr_len(ip4_hdr, layers[i].hdr_len); + ipv4_hdr_set_total_len(ip4_hdr, layers[i].hdr_len + curr_layer_payload_len); + ip4_hdr->ip_sum = 0; + ip4_hdr->ip_sum = checksum((const char *)ip4_hdr, layers[i].hdr_len); + curr_layer_payload_len += layers[i].hdr_len; + break; + case LAYER_PROTO_IPV6: + ip6_hdr = (struct ip6_hdr *)(new_pkt_data + new_pkt_len - layers[i].hdr_len - curr_layer_payload_len); + // update the checksums of the upper layer + if (i + 1 < layer_count && layers[i + 1].proto == LAYER_PROTO_TCP) + { + tcp_hdr = (struct tcphdr *)(new_pkt_data + new_pkt_len - curr_layer_payload_len); + tcp_hdr->th_sum = checksum_v6(tcp_hdr, curr_layer_payload_len, IPPROTO_TCP, &ip6_hdr->ip6_src, &ip6_hdr->ip6_dst); + } + if (i + 1 < layer_count && layers[i + 1].proto == LAYER_PROTO_UDP) + { + udp_hdr = (struct udphdr *)(new_pkt_data + new_pkt_len - curr_layer_payload_len); + udp_hdr->uh_sum = checksum_v6(udp_hdr, curr_layer_payload_len, IPPROTO_UDP, &ip6_hdr->ip6_src, &ip6_hdr->ip6_dst); + } + // update the IPv6 header + ipv6_hdr_set_payload_len(ip6_hdr, layers[i].hdr_len + curr_layer_payload_len - sizeof(struct ip6_hdr)); + curr_layer_payload_len += layers[i].hdr_len; + break; + case LAYER_PROTO_GRE: + // TODO + curr_layer_payload_len += layers[i].hdr_len; + break; + default: + curr_layer_payload_len += layers[i].hdr_len; + break; + } + } + + packet_parse(new_pkt, new_pkt_data, new_pkt_len); + // no metadata for the new packet from scratch + new_pkt->meta.origin_ctx = NULL; + + return new_pkt; +} \ No newline at end of file diff --git a/src/packet/packet_build.h b/src/packet/packet_build.h index dee6078..7391016 100644 --- a/src/packet/packet_build.h +++ b/src/packet/packet_build.h @@ -9,6 +9,7 @@ extern "C" struct packet *imitate_tcp_packet(const struct packet *origin_pkt, uint32_t tcp_seq, uint32_t tcp_ack, uint8_t tcp_flags, const char *tcp_payload, uint16_t tcp_payload_len); struct packet *imitate_udp_packet(const struct packet *origin_pkt, const char *udp_payload, uint16_t udp_payload_len); +struct packet *craft_packet_from_scratch(const struct layer larers[], uint16_t layer_count, const char *payload, uint16_t payload_len); #ifdef __cplusplus } diff --git a/src/stellar/version.map b/src/stellar/version.map index 1911222..9fb6e1e 100644 --- a/src/stellar/version.map +++ b/src/stellar/version.map @@ -16,6 +16,7 @@ global: packet_get_payload_len; imitate_tcp_packet; imitate_udp_packet; + craft_packet_from_scratch; session_exdata_free; stellar_session_exdata_new_index; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 96b01a1..3527117 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,2 +1,2 @@ add_subdirectory(packet_inject) -add_subdirectory(packet_parser) \ No newline at end of file +add_subdirectory(packet_tool) \ No newline at end of file diff --git a/test/packet_parser/packet_parser.cpp b/test/packet_parser/packet_parser.cpp deleted file mode 100644 index 52f2c02..0000000 --- a/test/packet_parser/packet_parser.cpp +++ /dev/null @@ -1,303 +0,0 @@ -#include -#include -#include "packet_def.h" -#include "packet_layer.h" -#include "packet_parse.h" -#include "eth_utils.h" -#include "vlan_utils.h" -#include "ipv4_utils.h" -#include "ipv6_utils.h" -#include "tcp_utils.h" -#include "udp_utils.h" - -struct options -{ - char *file; - int print_tshark_format; - int print_readable_format; -}; - -static uint64_t number = 0; - -#define MAX_BUFF_SIZE 2048 -struct buffer -{ - char buff[MAX_BUFF_SIZE]; - int used; - char elimiter; -}; - -static void buffer_push(struct buffer *buff, const char *str) -{ - int len = strlen(str); - if (buff->used + len + 1 >= MAX_BUFF_SIZE) - { - return; - } - if (buff->used) - { - buff->buff[buff->used++] = buff->elimiter; - } - memcpy(buff->buff + buff->used, str, len); - buff->used += len; -} - -static void packet_to_tshark_format(const struct packet *pkt, uint64_t idx) -{ - /* - tshark -r ${pcap} -T fields \ - -e frame.number \ - -e frame.protocols \ - -e eth.src \ - -e eth.dst \ - -e vlan.id \ - -e ip.src \ - -e ip.dst \ - -e ipv6.src \ - -e ipv6.dst \ - -e tcp.srcport \ - -e tcp.dstport \ - -e udp.srcport \ - -e udp.dstport \ - >> tshark_output.txt - */ - - struct buffer buff_proto = {.buff = {0}, .used = 0, .elimiter = ':'}; - struct buffer buff_eth_src = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_eth_dst = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_vlan_id = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_ipv4_src = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_ipv4_dst = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_ipv6_src = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_ipv6_dst = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_tcp_src = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_tcp_dst = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_udp_src = {.buff = {0}, .used = 0, .elimiter = ','}; - struct buffer buff_udp_dst = {.buff = {0}, .used = 0, .elimiter = ','}; - const struct ethhdr *eth_hdr = NULL; - const struct vlan_hdr *vlan_hdr = NULL; - const struct ip *ipv4_hdr = NULL; - const struct ip6_hdr *ipv6_hdr = NULL; - const struct tcphdr *tcp_hdr = NULL; - const struct udphdr *udp_hdr = NULL; - struct in_addr src_addr_v4 = {0}; - struct in_addr dst_addr_v4 = {0}; - struct in6_addr src_addr_v6 = {0}; - struct in6_addr dst_addr_v6 = {0}; - uint16_t src_port = 0; - uint16_t dst_port = 0; - uint16_t vlan_id = 0; - char tmp_src_buff[256] = {0}; - char tmp_dst_buff[256] = {0}; - - int num = packet_get_layer_count(pkt); - for (int i = 0; i < num; i++) - { - memset(tmp_src_buff, 0, sizeof(tmp_src_buff)); - memset(tmp_dst_buff, 0, sizeof(tmp_dst_buff)); - const struct raw_layer *layer = packet_get_raw_layer(pkt, i); - switch (layer->proto) - { - case LAYER_PROTO_ETHER: - buffer_push(&buff_proto, "eth:ethertype"); - eth_hdr = (const struct ethhdr *)layer->hdr_ptr; - eth_hdr_get_source(eth_hdr, tmp_src_buff, sizeof(tmp_src_buff)); - eth_hdr_get_dest(eth_hdr, tmp_dst_buff, sizeof(tmp_dst_buff)); - buffer_push(&buff_eth_src, tmp_src_buff); - buffer_push(&buff_eth_dst, tmp_dst_buff); - break; - case LAYER_PROTO_PWETH: - buffer_push(&buff_proto, "pwethheuristic:pwethcw"); - break; - case LAYER_PROTO_PPP: - buffer_push(&buff_proto, "ppp"); - break; - case LAYER_PROTO_L2TP: - buffer_push(&buff_proto, "l2tp"); - break; - case LAYER_PROTO_VLAN: - buffer_push(&buff_proto, "vlan:ethertype"); - vlan_hdr = (const struct vlan_hdr *)layer->hdr_ptr; - vlan_id = vlan_hdr_get_vid(vlan_hdr); - snprintf(tmp_src_buff, sizeof(tmp_src_buff), "%u", vlan_id); - buffer_push(&buff_vlan_id, tmp_src_buff); - break; - case LAYER_PROTO_PPPOE: - buffer_push(&buff_proto, "pppoes"); - break; - case LAYER_PROTO_MPLS: - buffer_push(&buff_proto, "mpls"); - break; - case LAYER_PROTO_IPV4: - buffer_push(&buff_proto, "ip"); - ipv4_hdr = (const struct ip *)layer->hdr_ptr; - src_addr_v4 = ipv4_hdr_get_src_in_addr(ipv4_hdr); - dst_addr_v4 = ipv4_hdr_get_dst_in_addr(ipv4_hdr); - inet_ntop(AF_INET, &src_addr_v4, tmp_src_buff, sizeof(tmp_src_buff)); - inet_ntop(AF_INET, &dst_addr_v4, tmp_dst_buff, sizeof(tmp_dst_buff)); - buffer_push(&buff_ipv4_src, tmp_src_buff); - buffer_push(&buff_ipv4_dst, tmp_dst_buff); - break; - case LAYER_PROTO_IPV6: - buffer_push(&buff_proto, "ipv6"); - ipv6_hdr = (const struct ip6_hdr *)layer->hdr_ptr; - switch (ipv6_hdr_get_next_header(ipv6_hdr)) - { - case IPPROTO_HOPOPTS: - buffer_push(&buff_proto, "ipv6.hopopts"); - break; - case IPPROTO_ROUTING: - buffer_push(&buff_proto, "ipv6.routing"); - break; - case IPPROTO_AH: - buffer_push(&buff_proto, "ah"); - break; - case IPPROTO_DSTOPTS: - buffer_push(&buff_proto, "ipv6.dstopts"); - break; - case IPPROTO_FRAGMENT: - buffer_push(&buff_proto, "ipv6.fraghdr"); - break; - default: - break; - } - src_addr_v6 = ipv6_hdr_get_src_in6_addr(ipv6_hdr); - dst_addr_v6 = ipv6_hdr_get_dst_in6_addr(ipv6_hdr); - inet_ntop(AF_INET6, &src_addr_v6, tmp_src_buff, sizeof(tmp_src_buff)); - inet_ntop(AF_INET6, &dst_addr_v6, tmp_dst_buff, sizeof(tmp_dst_buff)); - buffer_push(&buff_ipv6_src, tmp_src_buff); - buffer_push(&buff_ipv6_dst, tmp_dst_buff); - break; - case LAYER_PROTO_IPAH: - buffer_push(&buff_proto, "ah"); - break; - case LAYER_PROTO_GRE: - buffer_push(&buff_proto, "gre"); - break; - case LAYER_PROTO_UDP: - buffer_push(&buff_proto, "udp"); - udp_hdr = (const struct udphdr *)layer->hdr_ptr; - src_port = udp_hdr_get_src_port(udp_hdr); - dst_port = udp_hdr_get_dst_port(udp_hdr); - snprintf(tmp_src_buff, sizeof(tmp_src_buff), "%u", src_port); - snprintf(tmp_dst_buff, sizeof(tmp_dst_buff), "%u", dst_port); - buffer_push(&buff_udp_src, tmp_src_buff); - buffer_push(&buff_udp_dst, tmp_dst_buff); - break; - case LAYER_PROTO_TCP: - buffer_push(&buff_proto, "tcp"); - tcp_hdr = (const struct tcphdr *)layer->hdr_ptr; - src_port = tcp_hdr_get_src_port(tcp_hdr); - dst_port = tcp_hdr_get_dst_port(tcp_hdr); - snprintf(tmp_src_buff, sizeof(tmp_src_buff), "%u", src_port); - snprintf(tmp_dst_buff, sizeof(tmp_dst_buff), "%u", dst_port); - buffer_push(&buff_tcp_src, tmp_src_buff); - buffer_push(&buff_tcp_dst, tmp_dst_buff); - break; - case LAYER_PROTO_ICMP: - buffer_push(&buff_proto, "icmp"); - break; - case LAYER_PROTO_ICMP6: - buffer_push(&buff_proto, "icmpv6"); - break; - case LAYER_PROTO_VXLAN: - buffer_push(&buff_proto, "vxlan"); - break; - case LAYER_PROTO_GTP: - buffer_push(&buff_proto, "gtp"); - break; - default: - buffer_push(&buff_proto, "unknown"); - break; - } - } - - printf("%lu\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", - number, - buff_proto.buff, - buff_eth_src.buff, - buff_eth_dst.buff, - buff_vlan_id.buff, - buff_ipv4_src.buff, - buff_ipv4_dst.buff, - buff_ipv6_src.buff, - buff_ipv6_dst.buff, - buff_tcp_src.buff, - buff_tcp_dst.buff, - buff_udp_src.buff, - buff_udp_dst.buff); -} - -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; - memset(&pkt, 0, sizeof(pkt)); - packet_parse(&pkt, (const char *)bytes, h->caplen); - number++; - - if (opts->print_readable_format) - { - printf("\033[0;32m frame=%lu len=%u \033[0m", number, h->caplen); - packet_print(&pkt); - } - - if (opts->print_tshark_format) - { - packet_to_tshark_format(&pkt, number); - } -} - -static void usage(char *cmd) -{ - printf("Usage: %s\n", cmd); - printf("Options:\n"); - printf(" -f pcap file\n"); - printf(" -t print tshark format\n"); - printf(" -r print readable format\n"); - printf(" -h print help\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int opt = 0; - struct options opts = {0}; - while ((opt = getopt(argc, argv, "f:trh")) != -1) - { - switch (opt) - { - case 'f': - opts.file = optarg; - break; - case 't': - opts.print_tshark_format = 1; - break; - case 'r': - opts.print_readable_format = 1; - break; - case 'h': - default: - usage(argv[0]); - return 0; - } - } - - if (opts.file == NULL) - { - usage(argv[0]); - return -1; - } - - 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, (u_char *)&opts); - pcap_close(pcap); - - return 0; -} \ No newline at end of file diff --git a/test/packet_parser/CMakeLists.txt b/test/packet_tool/CMakeLists.txt similarity index 52% rename from test/packet_parser/CMakeLists.txt rename to test/packet_tool/CMakeLists.txt index 11a1c7a..bde3fd5 100644 --- a/test/packet_parser/CMakeLists.txt +++ b/test/packet_tool/CMakeLists.txt @@ -1,6 +1,5 @@ -# build packet_parser -add_executable(packet_parser packet_parser.cpp) -target_link_libraries(packet_parser packet pcap) +add_executable(packet_tool packet_tool.cpp) +target_link_libraries(packet_tool 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_tool/cmp_layers.sh similarity index 89% rename from test/packet_parser/cmp_layers.sh rename to test/packet_tool/cmp_layers.sh index 923b818..12ee76a 100644 --- a/test/packet_parser/cmp_layers.sh +++ b/test/packet_tool/cmp_layers.sh @@ -122,8 +122,8 @@ function preprocess_tshark_ouput() { done } -# When MPLS is nested, packet_parser will output multiple mpls fields, and tshark will only output one mpls field, so we need to preprocess the output -function preprocess_parser_ouput() { +# When MPLS is nested, packet_tool will output multiple mpls fields, and tshark will only output one mpls field, so we need to preprocess the output +function preprocess_tool_ouput() { input_file=$1 output_file=$2 cp ${input_file} ${output_file} @@ -167,13 +167,13 @@ for pcap in "${pcap_files[@]}"; do -e udp.dstport \ >>${tmp_file_dir}/tshark_output.txt - # packet_parser output - ./packet_parser -f ${pcap} -t >>${tmp_file_dir}/parser_output.txt + # packet_tool output + ./packet_tool -f ${pcap} -t >>${tmp_file_dir}/tool_output.txt - # compare tshark and packet_parser output + # compare tshark and packet_tool output preprocess_tshark_ouput ${tmp_file_dir}/tshark_output.txt ${tmp_file_dir}/tshark_format.txt - preprocess_parser_ouput ${tmp_file_dir}/parser_output.txt ${tmp_file_dir}/parser_format.txt - diff ${tmp_file_dir}/tshark_format.txt ${tmp_file_dir}/parser_format.txt >>${tmp_file_dir}/diff.txt + preprocess_tool_ouput ${tmp_file_dir}/tool_output.txt ${tmp_file_dir}/tool_format.txt + diff ${tmp_file_dir}/tshark_format.txt ${tmp_file_dir}/tool_format.txt >>${tmp_file_dir}/diff.txt # print result line_count=$(cat ${tmp_file_dir}/diff.txt | wc -l) diff --git a/test/packet_tool/packet_tool.cpp b/test/packet_tool/packet_tool.cpp new file mode 100644 index 0000000..e1ec6d3 --- /dev/null +++ b/test/packet_tool/packet_tool.cpp @@ -0,0 +1,434 @@ +#include +#include + +#include "eth_utils.h" +#include "vlan_utils.h" +#include "ipv4_utils.h" +#include "ipv6_utils.h" +#include "tcp_utils.h" +#include "udp_utils.h" +#include "packet_def.h" +#include "packet_layer.h" +#include "packet_parse.h" +#include "packet_utils.h" + +#define MAX_BUFF_SIZE 2048 +#define PRINT_GREEN(fmt, ...) printf("\033[0;32m" fmt "\033[0m\n", ##__VA_ARGS__) +#define PRINT_RED(fmt, ...) printf("\033[0;31m" fmt "\033[0m\n", ##__VA_ARGS__) + +struct str_buff +{ + char data[MAX_BUFF_SIZE]; + int used; + char elimiter; +}; + +struct runtime +{ + char *pcap_file; + uint64_t pcap_count; + int tshark_format; + int print_verbose; + int craft_compare; +}; + +static void str_buff_push(struct str_buff *buff, const char *str) +{ + int len = strlen(str); + if (buff->used + len + 1 >= MAX_BUFF_SIZE) + { + return; + } + if (buff->used) + { + buff->data[buff->used++] = buff->elimiter; + } + memcpy(buff->data + buff->used, str, len); + buff->used += len; +} + +static void dump_pcap(const char *file, const char *data, uint16_t len) +{ + struct pcap_pkt_hdr + { + unsigned int tv_sec; // time stamp + unsigned int tv_usec; // time stamp + unsigned int caplen; // length of portion present + unsigned int len; // length this packet (off wire) + } pcap_pkt_hdr = {0}; + + struct pcap_file_hdr + { + unsigned int magic; + unsigned short version_major; + unsigned short version_minor; + unsigned int thiszone; // gmt to local correction + unsigned int sigfigs; // accuracy of timestamps + unsigned int snaplen; // max length saved portion of each pkt + unsigned int linktype; // data link type (LINKTYPE_*) + } pcap_file_hdr = { + .magic = 0xA1B2C3D4, + .version_major = 0x0002, + .version_minor = 0x0004, + .thiszone = 0, + .sigfigs = 0, + .snaplen = 0xFFFF, + .linktype = 1}; + + if (file == NULL || data == NULL || len == 0) + { + return; + } + + FILE *fp = fopen(file, "w+"); + if (fp == NULL) + { + return; + } + + struct timeval ts = {0}; + gettimeofday(&ts, NULL); + + pcap_pkt_hdr.tv_sec = ts.tv_sec; + pcap_pkt_hdr.tv_usec = ts.tv_usec; + pcap_pkt_hdr.caplen = len; + pcap_pkt_hdr.len = len; + + fwrite(&pcap_file_hdr, sizeof(struct pcap_file_hdr), 1, fp); + fwrite(&pcap_pkt_hdr, sizeof(struct pcap_pkt_hdr), 1, fp); + fwrite(data, 1, len, fp); + fflush(fp); + fclose(fp); +} + +static void tshark_format(const struct runtime *rte, const struct packet *pkt) +{ + /* + tshark -r ${pcap} -T fields \ + -e frame.number \ + -e frame.protocols \ + -e eth.src \ + -e eth.dst \ + -e vlan.id \ + -e ip.src \ + -e ip.dst \ + -e ipv6.src \ + -e ipv6.dst \ + -e tcp.srcport \ + -e tcp.dstport \ + -e udp.srcport \ + -e udp.dstport \ + >> tshark_output.txt + */ + + struct str_buff buff_proto = {.data = {0}, .used = 0, .elimiter = ':'}; + struct str_buff buff_eth_src = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_eth_dst = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_vlan_id = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_ipv4_src = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_ipv4_dst = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_ipv6_src = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_ipv6_dst = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_tcp_src = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_tcp_dst = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_udp_src = {.data = {0}, .used = 0, .elimiter = ','}; + struct str_buff buff_udp_dst = {.data = {0}, .used = 0, .elimiter = ','}; + + const struct ethhdr *eth_hdr = NULL; + const struct vlan_hdr *vlan_hdr = NULL; + const struct ip *ip4_hdr = NULL; + const struct ip6_hdr *ip6_hdr = NULL; + const struct tcphdr *tcp_hdr = NULL; + const struct udphdr *udp_hdr = NULL; + + struct in_addr src_addr_v4 = {0}; + struct in_addr dst_addr_v4 = {0}; + struct in6_addr src_addr_v6 = {0}; + struct in6_addr dst_addr_v6 = {0}; + + uint16_t src_port = 0; + uint16_t dst_port = 0; + uint16_t vlan_id = 0; + + char tmp_src_buff[256] = {0}; + char tmp_dst_buff[256] = {0}; + + int num = packet_get_layer_count(pkt); + for (int i = 0; i < num; i++) + { + memset(tmp_src_buff, 0, sizeof(tmp_src_buff)); + memset(tmp_dst_buff, 0, sizeof(tmp_dst_buff)); + const struct raw_layer *layer = packet_get_raw_layer(pkt, i); + switch (layer->proto) + { + case LAYER_PROTO_ETHER: + str_buff_push(&buff_proto, "eth:ethertype"); + + eth_hdr = (const struct ethhdr *)layer->hdr_ptr; + eth_hdr_get_source(eth_hdr, tmp_src_buff, sizeof(tmp_src_buff)); + eth_hdr_get_dest(eth_hdr, tmp_dst_buff, sizeof(tmp_dst_buff)); + str_buff_push(&buff_eth_src, tmp_src_buff); + str_buff_push(&buff_eth_dst, tmp_dst_buff); + break; + case LAYER_PROTO_PWETH: + str_buff_push(&buff_proto, "pwethheuristic:pwethcw"); + break; + case LAYER_PROTO_PPP: + str_buff_push(&buff_proto, "ppp"); + break; + case LAYER_PROTO_L2TP: + str_buff_push(&buff_proto, "l2tp"); + break; + case LAYER_PROTO_VLAN: + str_buff_push(&buff_proto, "vlan:ethertype"); + + vlan_hdr = (const struct vlan_hdr *)layer->hdr_ptr; + vlan_id = vlan_hdr_get_vid(vlan_hdr); + snprintf(tmp_src_buff, sizeof(tmp_src_buff), "%u", vlan_id); + str_buff_push(&buff_vlan_id, tmp_src_buff); + break; + case LAYER_PROTO_PPPOE: + str_buff_push(&buff_proto, "pppoes"); + break; + case LAYER_PROTO_MPLS: + str_buff_push(&buff_proto, "mpls"); + break; + case LAYER_PROTO_IPV4: + str_buff_push(&buff_proto, "ip"); + + ip4_hdr = (const struct ip *)layer->hdr_ptr; + src_addr_v4 = ipv4_hdr_get_src_in_addr(ip4_hdr); + dst_addr_v4 = ipv4_hdr_get_dst_in_addr(ip4_hdr); + inet_ntop(AF_INET, &src_addr_v4, tmp_src_buff, sizeof(tmp_src_buff)); + inet_ntop(AF_INET, &dst_addr_v4, tmp_dst_buff, sizeof(tmp_dst_buff)); + str_buff_push(&buff_ipv4_src, tmp_src_buff); + str_buff_push(&buff_ipv4_dst, tmp_dst_buff); + break; + case LAYER_PROTO_IPV6: + str_buff_push(&buff_proto, "ipv6"); + ip6_hdr = (const struct ip6_hdr *)layer->hdr_ptr; + switch (ipv6_hdr_get_next_header(ip6_hdr)) + { + case IPPROTO_HOPOPTS: + str_buff_push(&buff_proto, "ipv6.hopopts"); + break; + case IPPROTO_ROUTING: + str_buff_push(&buff_proto, "ipv6.routing"); + break; + case IPPROTO_AH: + str_buff_push(&buff_proto, "ah"); + break; + case IPPROTO_DSTOPTS: + str_buff_push(&buff_proto, "ipv6.dstopts"); + break; + case IPPROTO_FRAGMENT: + str_buff_push(&buff_proto, "ipv6.fraghdr"); + break; + default: + break; + } + + src_addr_v6 = ipv6_hdr_get_src_in6_addr(ip6_hdr); + dst_addr_v6 = ipv6_hdr_get_dst_in6_addr(ip6_hdr); + inet_ntop(AF_INET6, &src_addr_v6, tmp_src_buff, sizeof(tmp_src_buff)); + inet_ntop(AF_INET6, &dst_addr_v6, tmp_dst_buff, sizeof(tmp_dst_buff)); + str_buff_push(&buff_ipv6_src, tmp_src_buff); + str_buff_push(&buff_ipv6_dst, tmp_dst_buff); + break; + case LAYER_PROTO_IPAH: + str_buff_push(&buff_proto, "ah"); + break; + case LAYER_PROTO_GRE: + str_buff_push(&buff_proto, "gre"); + break; + case LAYER_PROTO_UDP: + str_buff_push(&buff_proto, "udp"); + + udp_hdr = (const struct udphdr *)layer->hdr_ptr; + src_port = udp_hdr_get_src_port(udp_hdr); + dst_port = udp_hdr_get_dst_port(udp_hdr); + snprintf(tmp_src_buff, sizeof(tmp_src_buff), "%u", src_port); + snprintf(tmp_dst_buff, sizeof(tmp_dst_buff), "%u", dst_port); + str_buff_push(&buff_udp_src, tmp_src_buff); + str_buff_push(&buff_udp_dst, tmp_dst_buff); + break; + case LAYER_PROTO_TCP: + str_buff_push(&buff_proto, "tcp"); + + tcp_hdr = (const struct tcphdr *)layer->hdr_ptr; + src_port = tcp_hdr_get_src_port(tcp_hdr); + dst_port = tcp_hdr_get_dst_port(tcp_hdr); + snprintf(tmp_src_buff, sizeof(tmp_src_buff), "%u", src_port); + snprintf(tmp_dst_buff, sizeof(tmp_dst_buff), "%u", dst_port); + str_buff_push(&buff_tcp_src, tmp_src_buff); + str_buff_push(&buff_tcp_dst, tmp_dst_buff); + break; + case LAYER_PROTO_ICMP: + str_buff_push(&buff_proto, "icmp"); + break; + case LAYER_PROTO_ICMP6: + str_buff_push(&buff_proto, "icmpv6"); + break; + case LAYER_PROTO_VXLAN: + str_buff_push(&buff_proto, "vxlan"); + break; + case LAYER_PROTO_GTP: + str_buff_push(&buff_proto, "gtp"); + break; + default: + str_buff_push(&buff_proto, "unknown"); + break; + } + } + + printf("%lu\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", + rte->pcap_count, + buff_proto.data, + buff_eth_src.data, + buff_eth_dst.data, + buff_vlan_id.data, + buff_ipv4_src.data, + buff_ipv4_dst.data, + buff_ipv6_src.data, + buff_ipv6_dst.data, + buff_tcp_src.data, + buff_tcp_dst.data, + buff_udp_src.data, + buff_udp_dst.data); +} + +static void craft_compare(const struct runtime *rte, const struct packet *raw_pkt) +{ + struct layer layers[PACKET_MAX_LAYERS]; + int layer_count = PACKET_GETALL_LAYERS(raw_pkt, layers); + + struct packet *new_pkt = craft_packet_from_scratch(layers, layer_count, packet_get_payload(raw_pkt), packet_get_payload_len(raw_pkt)); + if (new_pkt == NULL) + { + PRINT_RED("craft compare: failed (craft error)"); + return; + } + + if (rte->print_verbose) + { + packet_print(new_pkt); + } + + if (rte->tshark_format) + { + tshark_format(rte, new_pkt); + } + + const char *raw_pkt_data = packet_get_raw_data(raw_pkt); + const char *new_pkt_data = packet_get_raw_data(new_pkt); + uint16_t raw_pkt_len = packet_get_raw_len(raw_pkt); + uint16_t new_pkt_len = packet_get_raw_len(new_pkt); + + if (raw_pkt_len != new_pkt_len) + { + PRINT_RED("craft compare: failed (length mismatch)"); + goto error_out; + } + + if (memcmp(raw_pkt_data, new_pkt_data, raw_pkt_len) != 0) + { + PRINT_RED("craft compare: failed (data mismatch)"); + goto error_out; + } + + PRINT_GREEN("craft compare: success"); + +error_out: + char file[256] = {0}; + snprintf(file, sizeof(file), "craft%lu.pcap", rte->pcap_count); + dump_pcap(file, new_pkt_data, new_pkt_len); + packet_free(new_pkt); +} + +static void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) +{ + struct runtime *rte = (struct runtime *)user; + + struct packet pkt; + memset(&pkt, 0, sizeof(pkt)); + packet_parse(&pkt, (const char *)bytes, h->caplen); + rte->pcap_count++; + + if (rte->print_verbose) + { + PRINT_GREEN("frame=%lu len=%u", rte->pcap_count, h->caplen); + packet_print(&pkt); + } + + if (rte->tshark_format) + { + tshark_format(rte, &pkt); + } + + if (rte->craft_compare) + { + craft_compare(rte, &pkt); + } +} + +static void usage(char *cmd) +{ + printf("Usage: %s\n", cmd); + printf("Options:\n"); + printf(" -f pcap file\n"); + printf(" -t print tshark format\n"); + printf(" -v print verbose info\n"); + printf(" -c compare recrafted packet\n"); + printf(" -h print help\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int opt = 0; + struct runtime rte = {0}; + while ((opt = getopt(argc, argv, "f:tvch")) != -1) + { + switch (opt) + { + case 'f': + rte.pcap_file = optarg; + break; + case 't': + rte.tshark_format = 1; + break; + case 'v': + rte.print_verbose = 1; + break; + case 'c': + rte.craft_compare = 1; + break; + case 'h': + default: + usage(argv[0]); + return 0; + } + } + + if (rte.pcap_file == NULL) + { + usage(argv[0]); + return -1; + } + + if (rte.print_verbose) + { + PRINT_GREEN("pcap=%s", rte.pcap_file); + } + + pcap_t *pcap = pcap_open_offline(rte.pcap_file, NULL); + if (pcap == NULL) + { + printf("pcap_open_offline() failed\n"); + return -1; + } + pcap_loop(pcap, -1, packet_handler, (u_char *)&rte); + pcap_close(pcap); + + return 0; +} \ No newline at end of file diff --git a/test/packet_parser/split_pcap.sh b/test/packet_tool/split_pcap.sh similarity index 100% rename from test/packet_parser/split_pcap.sh rename to test/packet_tool/split_pcap.sh