diff --git a/src/tests/system/network/tcp_shell/Jamfile b/src/tests/system/network/tcp_shell/Jamfile index 581458cb81..4046c5346a 100644 --- a/src/tests/system/network/tcp_shell/Jamfile +++ b/src/tests/system/network/tcp_shell/Jamfile @@ -26,7 +26,7 @@ SimpleTest tcp_shell : argv.c ipv4_address.cpp - : be libkernelland_emu.so [ TargetLibstdc++ ] + : be libkernelland_emu.so [ TargetLibstdc++ ] ; SimpleTest BufferQueueTest : diff --git a/src/tests/system/network/tcp_shell/pcap.h b/src/tests/system/network/tcp_shell/pcap.h new file mode 100644 index 0000000000..4f7ca8f53c --- /dev/null +++ b/src/tests/system/network/tcp_shell/pcap.h @@ -0,0 +1,36 @@ +/* + * Copyright 2023, Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef PCAP_H +#define PCAP_H + +#include + + +struct pcap_header { + uint32 magic; + uint16 version_major; + uint16 version_minor; + int32 timezone; + uint32 timestamp_accuracy; + uint32 max_packet_length; + uint32 linktype; +} _PACKED; + +#define PCAP_MAGIC (0xa1b2c3d4) + +#define PCAP_LINKTYPE_RAW (101) /* Raw IPv4/IPv6 */ +#define PCAP_LINKTYPE_IPV4 (228) +#define PCAP_LINKTYPE_IPV6 (229) + + +struct pcap_packet_header { + uint32 ts_sec; + uint32 ts_usec; + uint32 included_len; + uint32 original_len; +} _PACKED; + + +#endif // PCAP_H diff --git a/src/tests/system/network/tcp_shell/tcp_shell.cpp b/src/tests/system/network/tcp_shell/tcp_shell.cpp index e004e92cfb..f9ed88c016 100644 --- a/src/tests/system/network/tcp_shell/tcp_shell.cpp +++ b/src/tests/system/network/tcp_shell/tcp_shell.cpp @@ -9,6 +9,7 @@ #include "argv.h" #include "tcp.h" +#include "pcap.h" #include "utility.h" #include @@ -26,8 +27,11 @@ #include #include -#include #include +#include + +#include +#include #include #include #include @@ -95,7 +99,8 @@ static std::set sDropList; static bigtime_t sRoundTripTime = 0; static bool sIncreasingRoundTrip = false; static bool sRandomRoundTrip = false; -static bool sTCPDump = true; +static void (*sPacketMonitor)(net_buffer *, int32, bool) = NULL; +static int sPcapFD = -1; static bigtime_t sStartTime; static double sRandomReorder = 0.0; static std::set sReorderList; @@ -938,8 +943,6 @@ domain_get_mtu(net_protocol *protocol, const struct sockaddr *address) status_t domain_receive_data(net_buffer *buffer) { - static bigtime_t lastTime = 0; - uint32 packetNumber = atomic_add(&sPacketNumber, 1); bool drop = false; @@ -957,97 +960,8 @@ domain_receive_data(net_buffer *buffer) snooze(sRoundTripTime / 2 + add); } - if (sTCPDump) { - NetBufferHeaderReader bufferHeader(buffer); - if (bufferHeader.Status() < B_OK) - return bufferHeader.Status(); - - tcp_header &header = bufferHeader.Data(); - - bigtime_t now = system_time(); - if (lastTime == 0) - lastTime = now; - - printf("\33[0m% 3ld %8.6f (%8.6f) ", packetNumber, (now - sStartTime) / 1000000.0, - (now - lastTime) / 1000000.0); - lastTime = now; - - if (is_server((sockaddr *)buffer->source)) - printf("\33[31mserver > client: "); - else - printf("client > server: "); - - int32 length = buffer->size - header.HeaderLength(); - - if ((header.flags & TCP_FLAG_PUSH) != 0) - putchar('P'); - if ((header.flags & TCP_FLAG_SYNCHRONIZE) != 0) - putchar('S'); - if ((header.flags & TCP_FLAG_FINISH) != 0) - putchar('F'); - if ((header.flags & TCP_FLAG_RESET) != 0) - putchar('R'); - if ((header.flags - & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_PUSH | TCP_FLAG_RESET)) == 0) - putchar('.'); - - printf(" %lu:%lu (%lu)", header.Sequence(), header.Sequence() + length, length); - if ((header.flags & TCP_FLAG_ACKNOWLEDGE) != 0) - printf(" ack %lu", header.Acknowledge()); - - printf(" win %u", header.AdvertisedWindow()); - - if (header.HeaderLength() > sizeof(tcp_header)) { - int32 size = header.HeaderLength() - sizeof(tcp_header); - - tcp_option *option; - uint8 optionsBuffer[1024]; - if (gBufferModule->direct_access(buffer, sizeof(tcp_header), - size, (void **)&option) != B_OK) { - if (size > 1024) { - printf("options too large to take into account (%ld bytes)\n", size); - size = 1024; - } - - gBufferModule->read(buffer, sizeof(tcp_header), optionsBuffer, size); - option = (tcp_option *)optionsBuffer; - } - - while (size > 0) { - uint32 length = 1; - switch (option->kind) { - case TCP_OPTION_END: - case TCP_OPTION_NOP: - break; - case TCP_OPTION_MAX_SEGMENT_SIZE: - printf(" ", ntohs(option->max_segment_size)); - length = 4; - break; - case TCP_OPTION_WINDOW_SHIFT: - printf(" ", option->window_shift); - length = 3; - break; - case TCP_OPTION_TIMESTAMP: - printf(" ", option->timestamp.value, option->timestamp.reply); - length = 10; - break; - - default: - length = option->length; - // make sure we don't end up in an endless loop - if (length == 0) - size = 0; - break; - } - - size -= length; - option = (tcp_option *)((uint8 *)option + length); - } - } - - if (drop) - printf(" "); - printf("\33[0m\n"); + if (sPacketMonitor != NULL) { + sPacketMonitor(buffer, packetNumber, drop); } else if (drop) printf("<**** DROPPED %ld ****>\n", packetNumber); @@ -1113,6 +1027,183 @@ net_protocol_module_info gDomainModule = { }; +// #pragma mark - packet capture + + +static void +dump_printf(net_buffer* buffer, int32 packetNumber, bool willBeDropped) +{ + static bigtime_t lastTime = 0; + + NetBufferHeaderReader bufferHeader(buffer); + if (bufferHeader.Status() < B_OK) + return; + + tcp_header &header = bufferHeader.Data(); + + bigtime_t now = system_time(); + if (lastTime == 0) + lastTime = now; + + printf("\33[0m% 3ld %8.6f (%8.6f) ", packetNumber, (now - sStartTime) / 1000000.0, + (now - lastTime) / 1000000.0); + lastTime = now; + + if (is_server((sockaddr *)buffer->source)) + printf("\33[31mserver > client: "); + else + printf("client > server: "); + + int32 length = buffer->size - header.HeaderLength(); + + if ((header.flags & TCP_FLAG_PUSH) != 0) + putchar('P'); + if ((header.flags & TCP_FLAG_SYNCHRONIZE) != 0) + putchar('S'); + if ((header.flags & TCP_FLAG_FINISH) != 0) + putchar('F'); + if ((header.flags & TCP_FLAG_RESET) != 0) + putchar('R'); + if ((header.flags + & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH | TCP_FLAG_PUSH | TCP_FLAG_RESET)) == 0) + putchar('.'); + + printf(" %lu:%lu (%lu)", header.Sequence(), header.Sequence() + length, length); + if ((header.flags & TCP_FLAG_ACKNOWLEDGE) != 0) + printf(" ack %lu", header.Acknowledge()); + + printf(" win %u", header.AdvertisedWindow()); + + if (header.HeaderLength() > sizeof(tcp_header)) { + int32 size = header.HeaderLength() - sizeof(tcp_header); + + tcp_option *option; + uint8 optionsBuffer[1024]; + if (gBufferModule->direct_access(buffer, sizeof(tcp_header), + size, (void **)&option) != B_OK) { + if (size > 1024) { + printf("options too large to take into account (%ld bytes)\n", size); + size = 1024; + } + + gBufferModule->read(buffer, sizeof(tcp_header), optionsBuffer, size); + option = (tcp_option *)optionsBuffer; + } + + while (size > 0) { + uint32 length = 1; + switch (option->kind) { + case TCP_OPTION_END: + case TCP_OPTION_NOP: + break; + case TCP_OPTION_MAX_SEGMENT_SIZE: + printf(" ", ntohs(option->max_segment_size)); + length = 4; + break; + case TCP_OPTION_WINDOW_SHIFT: + printf(" ", option->window_shift); + length = 3; + break; + case TCP_OPTION_TIMESTAMP: + printf(" ", option->timestamp.value, option->timestamp.reply); + length = 10; + break; + + default: + length = option->length; + // make sure we don't end up in an endless loop + if (length == 0) + size = 0; + break; + } + + size -= length; + option = (tcp_option *)((uint8 *)option + length); + } + } + + if (willBeDropped) + printf(" "); + printf("\33[0m\n"); +} + + +static void +dump_pcap(net_buffer* buffer, int32 packetNumber, bool willBeDropped) +{ + const bigtime_t time = real_time_clock_usecs(); + + struct pcap_packet_header pcap_header; + pcap_header.ts_sec = time / 1000000; + pcap_header.ts_usec = time % 1000000; + pcap_header.included_len = sizeof(struct ip) + buffer->size; + pcap_header.original_len = pcap_header.included_len; + + struct ip ip_header; + ip_header.ip_v = IPVERSION; + ip_header.ip_hl = sizeof(struct ip) >> 2; + ip_header.ip_tos = 0; + ip_header.ip_len = htons(sizeof(struct ip) + buffer->size); + ip_header.ip_id = htons(packetNumber); + ip_header.ip_off = 0; + ip_header.ip_ttl = 254; + ip_header.ip_p = IPPROTO_TCP; + ip_header.ip_sum = 0; + ip_header.ip_src.s_addr = ((sockaddr_in*)buffer->source)->sin_addr.s_addr; + ip_header.ip_dst.s_addr = ((sockaddr_in*)buffer->destination)->sin_addr.s_addr; + + size_t count = 16, used = 0; + iovec vecs[count]; + + vecs[used].iov_base = &pcap_header; + vecs[used].iov_len = sizeof(pcap_header); + used++; + + vecs[used].iov_base = &ip_header; + vecs[used].iov_len = sizeof(ip_header); + used++; + + used += gNetBufferModule.get_iovecs(buffer, vecs + used, count - used); + + static mutex writesLock = MUTEX_INITIALIZER("pcap writes"); + MutexLocker _(writesLock); + ssize_t written = writev(sPcapFD, vecs, used); + if (written != (pcap_header.included_len + sizeof(pcap_packet_header))) { + fprintf(stderr, "writing to pcap file failed\n"); + exit(1); + } +} + + +static bool +setup_dump_pcap(const char* file) +{ + sPcapFD = open(file, O_CREAT | O_WRONLY | O_TRUNC); + if (sPcapFD < 0) { + fprintf(stderr, "tcp_shell: Failed to open output pcap file: %d\n", + errno); + return false; + } + + struct pcap_header header; + header.magic = PCAP_MAGIC; + header.version_major = 2; + header.version_minor = 4; + header.timezone = 0; + header.timestamp_accuracy = 0; + header.max_packet_length = 65535; + header.linktype = PCAP_LINKTYPE_IPV4; + if (write(sPcapFD, &header, sizeof(header)) != sizeof(header)) { + fprintf(stderr, "tcp_shell: Failed to write pcap file header: %d\n", + errno); + return false; + } + + sPacketMonitor = dump_pcap; + return true; +} + + // #pragma mark - test @@ -1286,7 +1377,7 @@ cleanup_context(struct context& context) } -// #pragma mark - +// #pragma mark - static void do_help(int argc, char** argv); @@ -1595,8 +1686,18 @@ do_help(int argc, char** argv) int -main(int argc, char** argv) +main(int argc, char* argv[]) { + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-w") == 0 && (i + 1) < argc) { + if (!setup_dump_pcap(argv[++i])) + return 1; + } + } + + if (sPacketMonitor == NULL) + sPacketMonitor = dump_printf; + status_t status = init_timers(); if (status < B_OK) { fprintf(stderr, "tcp_tester: Could not initialize timers: %s\n",