From 6bfb514df75c9e9636851af9386a8d96e315ec65 Mon Sep 17 00:00:00 2001 From: cozis Date: Tue, 29 Aug 2023 01:10:02 +0200 Subject: [PATCH] fixed i forgot about but seem to work --- makefile | 7 + src/arp.c | 39 ++-- src/arp.h | 35 +++- src/microtcp.c | 120 +----------- src/tcp.c | 2 + src/utils.c | 127 +++++++++++++ src/utils.h | 9 + tests/arp_0.test | 11 ++ tests/test_arp.c | 463 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 666 insertions(+), 147 deletions(-) create mode 100644 src/utils.c create mode 100644 src/utils.h create mode 100644 tests/arp_0.test create mode 100644 tests/test_arp.c diff --git a/makefile b/makefile index 14b056f..a5925f7 100644 --- a/makefile +++ b/makefile @@ -109,6 +109,8 @@ build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/* printf "\n#endif /* MICROTCP_BACKGROUND_THREAD */" >> $@ printf "\n#line 1 \"src/defs.h\"\n" >> $@ cat src/defs.h >> $@ + printf "\n#line 1 \"src/utils.h\"\n" >> $@ + cat src/utils.h >> $@ printf "\n#line 1 \"src/endian.h\"\n" >> $@ cat src/endian.h >> $@ printf "\n#line 1 \"src/arp.h\"\n" >> $@ @@ -121,6 +123,8 @@ build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/* cat src/tcp_timer.h >> $@ printf "\n#line 1 \"src/tcp.h\"\n" >> $@ cat src/tcp.h >> $@ + printf "\n#line 1 \"src/utils.c\"\n" >> $@ + cat src/utils.c >> $@ printf "\n#line 1 \"src/endian.c\"\n" >> $@ cat src/endian.c >> $@ printf "\n#line 1 \"src/arp.c\"\n" >> $@ @@ -142,6 +146,9 @@ build/echo_http: examples/microhttp/main.c $(LIBDIR)/libtuntap.a 3p/include/tunt build/test: tests/test.c tests/test_tcp_timer.c src/tcp_timer.c src/tcp_timer.h gcc -fprofile-arcs -ftest-coverage tests/test.c tests/test_tcp_timer.c src/tcp_timer.c -o $@ -Wall -Wextra -DTCP_MAX_TIMERS=4 +build/test_arp: tests/test_arp.c + gcc tests/test_arp.c -o build/test_arp -Wall -Wextra + report: build/test ./build/test gcov -b build/test-tcp_timer.c diff --git a/src/arp.c b/src/arp.c index 20fb26b..2a5e795 100644 --- a/src/arp.c +++ b/src/arp.c @@ -128,19 +128,6 @@ struct ethhdr { #define ARP_DEBUG_LOG(...) #endif -typedef enum { - ARP_HARDWARE_ETHERNET = 1, -} arp_hardware_type; - -typedef enum { - ARP_PROTOCOL_IP = 0x800, -} arp_protocol_type; - -typedef enum { - ARP_OPERATION_REQUEST = 1, - ARP_OPERATION_REPLY = 2, -} arp_operation_t; - void arp_change_output_buffer(arp_state_t *state, void *ptr, size_t max) { if (max < sizeof(arp_packet_t)) @@ -619,19 +606,23 @@ void arp_resolve_mac(arp_state_t *state, ip_address_t ip, void *userp, void (*ca append_pending_request_to_used_list(state, pending_request); arp_packet_t *packet = state->output; - packet->hardware_type = cpu_to_net_u16(ARP_HARDWARE_ETHERNET); - packet->protocol_type = cpu_to_net_u16(ARP_PROTOCOL_IP); - packet->hardware_len = 6; - packet->protocol_len = 4; - packet->operation_type = cpu_to_net_u16(ARP_OPERATION_REQUEST); - packet->sender_hardware_address = state->self_mac; - packet->sender_protocol_address = state->self_ip; - packet->target_hardware_address = MAC_ZERO; // This is what we're trying to find - packet->target_protocol_address = ip; + if (packet != NULL) { + packet->hardware_type = cpu_to_net_u16(ARP_HARDWARE_ETHERNET); + packet->protocol_type = cpu_to_net_u16(ARP_PROTOCOL_IP); + packet->hardware_len = 6; + packet->protocol_len = 4; + packet->operation_type = cpu_to_net_u16(ARP_OPERATION_REQUEST); + packet->sender_hardware_address = state->self_mac; + packet->sender_protocol_address = state->self_ip; + packet->target_hardware_address = MAC_ZERO; // This is what we're trying to find + packet->target_protocol_address = ip; - ARP_DEBUG_LOG("Sending out ARP request to resolve MAC"); + ARP_DEBUG_LOG("Sending out ARP request to resolve MAC"); - state->send(state->send_data, MAC_BROADCAST); + state->send(state->send_data, MAC_BROADCAST); + } else { + ARP_DEBUG_LOG("Couldn't send ARP request because no output buffer was provided"); + } } } diff --git a/src/arp.h b/src/arp.h index 70f4670..06e5164 100644 --- a/src/arp.h +++ b/src/arp.h @@ -42,6 +42,19 @@ struct arp_pending_request_t { void (*callback)(void*, arp_resolution_status_t status, mac_address_t); }; +typedef enum { + ARP_HARDWARE_ETHERNET = 1, +} arp_hardware_type; + +typedef enum { + ARP_PROTOCOL_IP = 0x800, +} arp_protocol_type; + +typedef enum { + ARP_OPERATION_REQUEST = 1, + ARP_OPERATION_REPLY = 2, +} arp_operation_t; + typedef struct __attribute__((__packed__)) { uint16_t hardware_type; uint16_t protocol_type; @@ -83,7 +96,7 @@ typedef struct { arp_pending_request_t *pending_request_free_list; arp_pending_request_t *pending_request_used_list; arp_pending_request_t *pending_request_used_tail; - arp_pending_request_t pending_request_pool[ARP_MAX_PENDING_REQUESTS]; + arp_pending_request_t pending_request_pool[ARP_MAX_PENDING_REQUESTS]; } arp_state_t; typedef enum { @@ -93,9 +106,23 @@ typedef enum { ARP_PROCESS_RESULT_OK, } arp_process_result_t; -void arp_init(arp_state_t *state, ip_address_t ip, mac_address_t mac, void *send_data, void (*send)(void*, mac_address_t)); +void arp_init(arp_state_t *state, + ip_address_t ip, + mac_address_t mac, + void *send_data, + void (*send)(void*, mac_address_t)); + void arp_free(arp_state_t *state); -arp_process_result_t arp_process_packet(arp_state_t *state, const void *packet, size_t len); -void arp_resolve_mac(arp_state_t *state, ip_address_t ip, void *userp, void (*callback)(void*, arp_resolution_status_t, mac_address_t)); + +arp_process_result_t +arp_process_packet(arp_state_t *state, + const void *packet, + size_t len); + +void arp_resolve_mac(arp_state_t *state, + ip_address_t ip, + void *userp, + void (*callback)(void*, arp_resolution_status_t, mac_address_t)); + void arp_seconds_passed(arp_state_t *state, size_t seconds); void arp_change_output_buffer(arp_state_t *state, void *ptr, size_t max); \ No newline at end of file diff --git a/src/microtcp.c b/src/microtcp.c index 996abd2..afbf44b 100644 --- a/src/microtcp.c +++ b/src/microtcp.c @@ -1,5 +1,4 @@ #include // time() -#include #include #include // strerror() #include @@ -9,6 +8,7 @@ # include "ip.h" # include "arp.h" # include "tcp.h" +# include "utils.h" # include "endian.h" # include "microtcp.h" # ifdef MICROTCP_BACKGROUND_THREAD @@ -435,124 +435,6 @@ static int loop(void *data) } #endif -static bool is_hex_digit(char c) -{ - return (c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f') - || (c >= 'A' && c <= 'F'); -} - -static int int_from_hex_digit(char c) -{ - assert(is_hex_digit(c)); - if (c >= 'A' || c <= 'F') - return c - 'A' + 10; - if (c >= 'a' || c <= 'f') - return c - 'a' + 10; - return c - '0'; -} - -static bool parse_mac(const char *src, size_t len, - mac_address_t *mac) -{ - if (src == NULL || len != 17 - || !is_hex_digit(src[0]) - || !is_hex_digit(src[1]) - || src[2] != ':' - || !is_hex_digit(src[3]) - || !is_hex_digit(src[4]) - || src[5] != ':' - || !is_hex_digit(src[6]) - || !is_hex_digit(src[7]) - || src[8] != ':' - || !is_hex_digit(src[9]) - || !is_hex_digit(src[10]) - || src[11] != ':' - || !is_hex_digit(src[12]) - || !is_hex_digit(src[13]) - || src[14] != ':' - || !is_hex_digit(src[15]) - || !is_hex_digit(src[16])) - return false; - - static const char max_char_map[] = "0123456789ABCDEF"; - - if (mac) { - mac->data[0] = max_char_map[int_from_hex_digit(src[ 0])] << 4 - | max_char_map[int_from_hex_digit(src[ 1])]; - mac->data[1] = max_char_map[int_from_hex_digit(src[ 3])] << 4 - | max_char_map[int_from_hex_digit(src[ 4])]; - mac->data[2] = max_char_map[int_from_hex_digit(src[ 6])] << 4 - | max_char_map[int_from_hex_digit(src[ 7])]; - mac->data[3] = max_char_map[int_from_hex_digit(src[ 9])] << 4 - | max_char_map[int_from_hex_digit(src[10])]; - mac->data[4] = max_char_map[int_from_hex_digit(src[12])] << 4 - | max_char_map[int_from_hex_digit(src[13])]; - mac->data[5] = max_char_map[int_from_hex_digit(src[15])] << 4 - | max_char_map[int_from_hex_digit(src[16])]; - } - return true; -} - -static mac_address_t generate_random_mac() -{ - mac_address_t mac = { - .data = { - rand() & 0xff, - rand() & 0xff, - rand() & 0xff, - rand() & 0xff, - rand() & 0xff, - rand() & 0xff, - }, - }; - return mac; -} - -static bool parse_ip(const char *ip, ip_address_t *parsed_ip) -{ - size_t len = strlen(ip); - size_t i = 0; - - uint32_t value = 0; - - for (size_t k = 0; k < 4; k++) { - if (i == len || !isdigit(ip[i])) - return false; - int n = 0; // Used to represent a byte, but it's larger - // to detect overflows. - do { - // Convert character to number - int digit = ip[i] - '0'; - if (n > (UINT8_MAX - digit)/10) - // Adding this digit would make the - // byte overflow, so it can't be part - // of the octet. - break; - n = n * 10 + digit; - i++; - } while (i < len && isdigit(ip[i])); - - assert(n >= 0 && n <= UINT8_MAX); - value = (value << 8) | (uint8_t) n; - - // If this isn't the last octet and there is no - // dot following it, the address is invalid. - if (k < 3) { - if (i == len || ip[i] != '.') - return false; - i++; // Consume the dot. - } - } - if (i < len) - // source string contains something - // other than the address in it. - return false; - - *parsed_ip = cpu_to_net_u32(value); - return true; -} - microtcp_t *microtcp_create_using_callbacks(const char *ip, const char *mac, microtcp_callbacks_t callbacks) { diff --git a/src/tcp.c b/src/tcp.c index 3bcb58c..7d1d725 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -409,6 +409,8 @@ static void timeout_callback_time_wait(void *data) tcp_connection_t *connection = data; assert(connection->state == TCP_STATE_TIME_WAIT); + TCP_DEBUG_LOG("TIME-WAIT -> CLOSED"); + // We can finally free up this connection structure! really_close_connection(connection); } diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..cae0940 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,127 @@ +#include +#include // rand +#include + +#ifndef MICROTCP_AMALGAMATION +#include "utils.h" +#include "endian.h" +#endif + +static bool is_hex_digit(char c) +{ + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); +} + +static int int_from_hex_digit(char c) +{ + assert(is_hex_digit(c)); + if (c >= 'A' || c <= 'F') + return c - 'A' + 10; + if (c >= 'a' || c <= 'f') + return c - 'a' + 10; + return c - '0'; +} + +bool parse_mac(const char *src, size_t len, + mac_address_t *mac) +{ + if (src == NULL || len != 17 + || !is_hex_digit(src[0]) + || !is_hex_digit(src[1]) + || src[2] != ':' + || !is_hex_digit(src[3]) + || !is_hex_digit(src[4]) + || src[5] != ':' + || !is_hex_digit(src[6]) + || !is_hex_digit(src[7]) + || src[8] != ':' + || !is_hex_digit(src[9]) + || !is_hex_digit(src[10]) + || src[11] != ':' + || !is_hex_digit(src[12]) + || !is_hex_digit(src[13]) + || src[14] != ':' + || !is_hex_digit(src[15]) + || !is_hex_digit(src[16])) + return false; + + static const char max_char_map[] = "0123456789ABCDEF"; + + if (mac) { + mac->data[0] = max_char_map[int_from_hex_digit(src[ 0])] << 4 + | max_char_map[int_from_hex_digit(src[ 1])]; + mac->data[1] = max_char_map[int_from_hex_digit(src[ 3])] << 4 + | max_char_map[int_from_hex_digit(src[ 4])]; + mac->data[2] = max_char_map[int_from_hex_digit(src[ 6])] << 4 + | max_char_map[int_from_hex_digit(src[ 7])]; + mac->data[3] = max_char_map[int_from_hex_digit(src[ 9])] << 4 + | max_char_map[int_from_hex_digit(src[10])]; + mac->data[4] = max_char_map[int_from_hex_digit(src[12])] << 4 + | max_char_map[int_from_hex_digit(src[13])]; + mac->data[5] = max_char_map[int_from_hex_digit(src[15])] << 4 + | max_char_map[int_from_hex_digit(src[16])]; + } + return true; +} + +mac_address_t generate_random_mac() +{ + mac_address_t mac = { + .data = { + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + rand() & 0xff, + }, + }; + return mac; +} + + +bool parse_ip(const char *ip, ip_address_t *parsed_ip) +{ + size_t len = strlen(ip); + size_t i = 0; + + uint32_t value = 0; + + for (size_t k = 0; k < 4; k++) { + if (i == len || !isdigit(ip[i])) + return false; + int n = 0; // Used to represent a byte, but it's larger + // to detect overflows. + do { + // Convert character to number + int digit = ip[i] - '0'; + if (n > (UINT8_MAX - digit)/10) + // Adding this digit would make the + // byte overflow, so it can't be part + // of the octet. + break; + n = n * 10 + digit; + i++; + } while (i < len && isdigit(ip[i])); + + assert(n >= 0 && n <= UINT8_MAX); + value = (value << 8) | (uint8_t) n; + + // If this isn't the last octet and there is no + // dot following it, the address is invalid. + if (k < 3) { + if (i == len || ip[i] != '.') + return false; + i++; // Consume the dot. + } + } + if (i < len) + // source string contains something + // other than the address in it. + return false; + + *parsed_ip = cpu_to_net_u32(value); + return true; +} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..6d0c7bb --- /dev/null +++ b/src/utils.h @@ -0,0 +1,9 @@ +#include + +#ifndef MICROTCP_AMALGAMATION +#include "defs.h" +#endif + +bool parse_mac(const char *src, size_t len, mac_address_t *mac); +bool parse_ip(const char *ip, ip_address_t *parsed_ip); +mac_address_t generate_random_mac(); diff --git a/tests/arp_0.test b/tests/arp_0.test new file mode 100644 index 0000000..1d3f173 --- /dev/null +++ b/tests/arp_0.test @@ -0,0 +1,11 @@ +@type [ARP] + +@desc + Peer requests host's MAC given its IP and host replies. + +@IP [10.0.0.4] +@MAC [56:34:f5:4d:4f:44] + +@timeline + <= [ ETHERNET | IP | 6 | 4 | REQUEST | 00:34:56:34:f5:4f | 10.0.0.5 | 00:00:00:00:00:00 | 10.0.0.4 ] + => [ ETHERNET | IP | 6 | 4 | REPLY | 56:34:f5:4d:4f:44 | 10.0.0.4 | 00:34:56:34:f5:4f | 10.0.0.5 ] diff --git a/tests/test_arp.c b/tests/test_arp.c new file mode 100644 index 0000000..14b63ba --- /dev/null +++ b/tests/test_arp.c @@ -0,0 +1,463 @@ +#include +#include +#include "../src/utils.c" +#include "../src/endian.c" +#include "../src/arp.c" + +/* + BLACK BOX TEST CASES + + A) Peer requests host's MAC given its IP and host + replies. + + B) Peer requests host's non MAC level 2 address + given its IP and host doesn't reply because it + only supports MAC. + + C) Peer requests host's MAC address given its non + IP level 3 address and host doesn't reply + because it only supports IP. + + D) Peer requests host's MAC given its IP, but the + MAC address length field isn't 6, therefore + host doesn't reply. + + E) Peer requests host's MAC given its IP, but the + IP address length field isn't 4, therefore host + doesn't reply. + + F) Peer sends a request/reply that doesn't refer + to host and is from a sender with IP never seen + by the host (no entry in the translation table). + It's expected that no entry is added to the + translation table. + + G) Peer sends a request/reply that doesn't refer + to host but is from a sender with IP already + in the ARP translation table, therefore is + expected that host updates the entry. + + H) Program queries the ARP module for a MAC that + isn't cached, therefore an ARP request is + expected to be generated and, when replied to, + the ARP module is expected to resolve the + program's query. + + I) Program queries the ARP module for a MAC that's + cached, therefore the ARP module is expected to + resolve it without sending packets. +*/ + +typedef enum { + TEST_PASSED, + TEST_FAILED, + TEST_ABORTED, +} test_result_t; + +#define OUTPUT_QUEUE_SIZE 8 + +typedef struct { + arp_state_t *state; + arp_packet_t queue[OUTPUT_QUEUE_SIZE]; + int count; + int oflow; +} output_queue_t; + +static void send_arp_packet_callback(void *data, mac_address_t mac) +{ + output_queue_t *oq = (output_queue_t*) data; + if (oq->count == OUTPUT_QUEUE_SIZE) + oq->oflow++; + else { + oq->count++; + if (oq->count == OUTPUT_QUEUE_SIZE) + arp_change_output_buffer(oq->state, NULL, 0); + else + arp_change_output_buffer(oq->state, oq->queue+oq->count, sizeof(arp_packet_t)); + } +} + +test_result_t test_arp_bb_A(char *msg, size_t msgmax) +{ + // Addresses of the packets that will be sent towards + // the ARP module + const char net_ip_str[] = "10.0.0.5"; + const char net_mac_str[] = "00:34:56:34:f5:4f"; + + // Addresses of the ARP module + const char ip_str[] = "10.0.0.4"; + const char mac_str[] = "56:34:f5:4d:4f:44"; + + // Parse the addresses to binary form + ip_address_t ip, net_ip; + mac_address_t mac, net_mac; + if (!parse_mac(mac_str, sizeof(mac_str)-1, &mac) || + !parse_ip(ip_str, &ip) || + !parse_mac(net_mac_str, sizeof(net_mac_str)-1, &net_mac) || + !parse_ip(net_ip_str, &net_ip)) { + snprintf(msg, msgmax, "Couldn't parse IP and MAC strings"); + return TEST_ABORTED; + } + + // Set up the module with the output queue + arp_state_t state; + output_queue_t oq = {.state=&state, .count=0, .oflow=0}; // Buffer where replies and requests will be stored + // by the ARP module + arp_init(&state, ip, mac, &oq, send_arp_packet_callback); + arp_change_output_buffer(&state, oq.queue, sizeof(arp_packet_t)); + + // Build the request + arp_packet_t request = { + .hardware_type = cpu_to_net_u16(ARP_HARDWARE_ETHERNET), + .protocol_type = cpu_to_net_u16(ARP_PROTOCOL_IP), + .hardware_len = 6, + .protocol_len = 4, + .operation_type = cpu_to_net_u16(ARP_OPERATION_REQUEST), + .sender_hardware_address = net_mac, + .sender_protocol_address = net_ip, + .target_hardware_address = MAC_ZERO, + .target_protocol_address = ip, + }; + + // Send the request + arp_process_result_t res; + res = arp_process_packet(&state, &request, sizeof(arp_packet_t)); + switch (res) { + case ARP_PROCESS_RESULT_HWARENOTSUPP: + case ARP_PROCESS_RESULT_PROTONOTSUPP: + case ARP_PROCESS_RESULT_INVALID: + snprintf(msg, msgmax, "ARP module couldn't process request"); + return TEST_FAILED; + + case ARP_PROCESS_RESULT_OK: + break; + } + + // Make sure that the module replies one time and one time only + if (oq.count == 0) { + // The ARP module sent no reply given + // our request. + snprintf(msg, msgmax, "ARP module didn't reply"); + return TEST_FAILED; + } + if (oq.count > 1) { + // Sent too many replies + snprintf(msg, msgmax, "ARP module replied too many times"); + return TEST_FAILED; + } + + // Check that the reply has the right content + arp_packet_t *reply = oq.queue; + if (net_to_cpu_u16(reply->hardware_type) != ARP_HARDWARE_ETHERNET || + net_to_cpu_u16(reply->protocol_type) != ARP_PROTOCOL_IP || + net_to_cpu_u16(reply->operation_type) != ARP_OPERATION_REPLY || + memcmp(&reply->sender_hardware_address, &mac, sizeof(mac_address_t)) || + memcmp(&reply->sender_protocol_address, &ip, sizeof(ip_address_t)) || + memcmp(&reply->target_hardware_address, &net_mac, sizeof(mac_address_t)) || + memcmp(&reply->target_protocol_address, &net_ip, sizeof(ip_address_t))) { + // Unexpected reply + snprintf(msg, msgmax, "ARP module sent an unexpected reply"); + return TEST_FAILED; + } + + return TEST_PASSED; +} + +test_result_t test_arp_bb_B(char *msg, size_t msgmax) +{ + // Addresses of the packets that will be sent towards + // the ARP module + const char net_ip_str[] = "10.0.0.5"; + const char net_mac_str[] = "00:34:56:34:f5:4f"; + + // Addresses of the ARP module + const char ip_str[] = "10.0.0.4"; + const char mac_str[] = "56:34:f5:4d:4f:44"; + + // Parse the addresses to binary form + ip_address_t ip, net_ip; + mac_address_t mac, net_mac; + if (!parse_mac(mac_str, sizeof(mac_str)-1, &mac) || + !parse_ip(ip_str, &ip) || + !parse_mac(net_mac_str, sizeof(net_mac_str)-1, &net_mac) || + !parse_ip(net_ip_str, &net_ip)) { + snprintf(msg, msgmax, "Couldn't parse IP and MAC strings"); + return TEST_ABORTED; + } + + // Set up the module with the output queue + arp_state_t state; + output_queue_t oq = {.state=&state, .count=0, .oflow=0}; // Buffer where replies and requests will be stored + // by the ARP module + arp_init(&state, ip, mac, &oq, send_arp_packet_callback); + arp_change_output_buffer(&state, oq.queue, sizeof(arp_packet_t)); + + // Build the request + arp_packet_t request = { + .hardware_type = cpu_to_net_u16(ARP_HARDWARE_ETHERNET)+1, // Some value other than ETHERNET + .protocol_type = cpu_to_net_u16(ARP_PROTOCOL_IP), + .hardware_len = 6, + .protocol_len = 4, + .operation_type = cpu_to_net_u16(ARP_OPERATION_REQUEST), + .sender_hardware_address = net_mac, + .sender_protocol_address = net_ip, + .target_hardware_address = MAC_ZERO, + .target_protocol_address = ip, + }; + + // Send the request + arp_process_result_t res; + res = arp_process_packet(&state, &request, sizeof(arp_packet_t)); + switch (res) { + case ARP_PROCESS_RESULT_HWARENOTSUPP: + break; + + case ARP_PROCESS_RESULT_PROTONOTSUPP: + case ARP_PROCESS_RESULT_INVALID: + snprintf(msg, msgmax, "ARP module couldn't process request"); + return TEST_FAILED; + + case ARP_PROCESS_RESULT_OK: + snprintf(msg, msgmax, "ARP module processed a request for an hardware type it didn't support"); + return TEST_FAILED; + } + + if (oq.count > 0) { + // Sent replies + snprintf(msg, msgmax, "ARP module replied even though it failed to process the request"); + return TEST_FAILED; + } + + return TEST_PASSED; +} + +test_result_t test_arp_bb_C(char *msg, size_t msgmax) +{ + // Addresses of the packets that will be sent towards + // the ARP module + const char net_ip_str[] = "10.0.0.5"; + const char net_mac_str[] = "00:34:56:34:f5:4f"; + + // Addresses of the ARP module + const char ip_str[] = "10.0.0.4"; + const char mac_str[] = "56:34:f5:4d:4f:44"; + + // Parse the addresses to binary form + ip_address_t ip, net_ip; + mac_address_t mac, net_mac; + if (!parse_mac(mac_str, sizeof(mac_str)-1, &mac) || + !parse_ip(ip_str, &ip) || + !parse_mac(net_mac_str, sizeof(net_mac_str)-1, &net_mac) || + !parse_ip(net_ip_str, &net_ip)) { + snprintf(msg, msgmax, "Couldn't parse IP and MAC strings"); + return TEST_ABORTED; + } + + // Set up the module with the output queue + arp_state_t state; + output_queue_t oq = {.state=&state, .count=0, .oflow=0}; // Buffer where replies and requests will be stored + // by the ARP module + arp_init(&state, ip, mac, &oq, send_arp_packet_callback); + arp_change_output_buffer(&state, oq.queue, sizeof(arp_packet_t)); + + // Build the request + arp_packet_t request = { + .hardware_type = cpu_to_net_u16(ARP_HARDWARE_ETHERNET), + .protocol_type = cpu_to_net_u16(ARP_PROTOCOL_IP)+1, // Some value other than IP + .hardware_len = 6, + .protocol_len = 4, + .operation_type = cpu_to_net_u16(ARP_OPERATION_REQUEST), + .sender_hardware_address = net_mac, + .sender_protocol_address = net_ip, + .target_hardware_address = MAC_ZERO, + .target_protocol_address = ip, + }; + + // Send the request + arp_process_result_t res; + res = arp_process_packet(&state, &request, sizeof(arp_packet_t)); + switch (res) { + + case ARP_PROCESS_RESULT_PROTONOTSUPP: + break; + + case ARP_PROCESS_RESULT_HWARENOTSUPP: + case ARP_PROCESS_RESULT_INVALID: + snprintf(msg, msgmax, "ARP module couldn't process request"); + return TEST_FAILED; + + case ARP_PROCESS_RESULT_OK: + snprintf(msg, msgmax, "ARP module processed a request for a protocol type it didn't support"); + return TEST_FAILED; + } + + if (oq.count > 0) { + // Sent replies + snprintf(msg, msgmax, "ARP module replied even though it failed to process the request"); + return TEST_FAILED; + } + + return TEST_PASSED; +} + + +test_result_t test_arp_bb_D(char *msg, size_t msgmax) +{ + // Addresses of the packets that will be sent towards + // the ARP module + const char net_ip_str[] = "10.0.0.5"; + const char net_mac_str[] = "00:34:56:34:f5:4f"; + + // Addresses of the ARP module + const char ip_str[] = "10.0.0.4"; + const char mac_str[] = "56:34:f5:4d:4f:44"; + + // Parse the addresses to binary form + ip_address_t ip, net_ip; + mac_address_t mac, net_mac; + if (!parse_mac(mac_str, sizeof(mac_str)-1, &mac) || + !parse_ip(ip_str, &ip) || + !parse_mac(net_mac_str, sizeof(net_mac_str)-1, &net_mac) || + !parse_ip(net_ip_str, &net_ip)) { + snprintf(msg, msgmax, "Couldn't parse IP and MAC strings"); + return TEST_ABORTED; + } + + // Set up the module with the output queue + arp_state_t state; + output_queue_t oq = {.state=&state, .count=0, .oflow=0}; // Buffer where replies and requests will be stored + // by the ARP module + arp_init(&state, ip, mac, &oq, send_arp_packet_callback); + arp_change_output_buffer(&state, oq.queue, sizeof(arp_packet_t)); + + // Build the request + arp_packet_t request = { + .hardware_type = cpu_to_net_u16(ARP_HARDWARE_ETHERNET), + .protocol_type = cpu_to_net_u16(ARP_PROTOCOL_IP), + .hardware_len = 6+1, // Something other than the correct length + .protocol_len = 4, + .operation_type = cpu_to_net_u16(ARP_OPERATION_REQUEST), + .sender_hardware_address = net_mac, + .sender_protocol_address = net_ip, + .target_hardware_address = MAC_ZERO, + .target_protocol_address = ip, + }; + + // Send the request + arp_process_result_t res; + res = arp_process_packet(&state, &request, sizeof(arp_packet_t)); + switch (res) { + + case ARP_PROCESS_RESULT_INVALID: + break; + + case ARP_PROCESS_RESULT_HWARENOTSUPP: + case ARP_PROCESS_RESULT_PROTONOTSUPP: + snprintf(msg, msgmax, "ARP module couldn't process request"); + return TEST_FAILED; + + case ARP_PROCESS_RESULT_OK: + snprintf(msg, msgmax, "ARP module processed a request for an invalid hardware address length"); + return TEST_FAILED; + } + + if (oq.count > 0) { + // Sent replies + snprintf(msg, msgmax, "ARP module replied even though it failed to process the request"); + return TEST_FAILED; + } + + return TEST_PASSED; +} + + +test_result_t test_arp_bb_E(char *msg, size_t msgmax) +{ + // Addresses of the packets that will be sent towards + // the ARP module + const char net_ip_str[] = "10.0.0.5"; + const char net_mac_str[] = "00:34:56:34:f5:4f"; + + // Addresses of the ARP module + const char ip_str[] = "10.0.0.4"; + const char mac_str[] = "56:34:f5:4d:4f:44"; + + // Parse the addresses to binary form + ip_address_t ip, net_ip; + mac_address_t mac, net_mac; + if (!parse_mac(mac_str, sizeof(mac_str)-1, &mac) || + !parse_ip(ip_str, &ip) || + !parse_mac(net_mac_str, sizeof(net_mac_str)-1, &net_mac) || + !parse_ip(net_ip_str, &net_ip)) { + snprintf(msg, msgmax, "Couldn't parse IP and MAC strings"); + return TEST_ABORTED; + } + + // Set up the module with the output queue + arp_state_t state; + output_queue_t oq = {.state=&state, .count=0, .oflow=0}; // Buffer where replies and requests will be stored + // by the ARP module + arp_init(&state, ip, mac, &oq, send_arp_packet_callback); + arp_change_output_buffer(&state, oq.queue, sizeof(arp_packet_t)); + + // Build the request + arp_packet_t request = { + .hardware_type = cpu_to_net_u16(ARP_HARDWARE_ETHERNET), + .protocol_type = cpu_to_net_u16(ARP_PROTOCOL_IP), + .hardware_len = 6, + .protocol_len = 4+1, // Something other than the correct length + .operation_type = cpu_to_net_u16(ARP_OPERATION_REQUEST), + .sender_hardware_address = net_mac, + .sender_protocol_address = net_ip, + .target_hardware_address = MAC_ZERO, + .target_protocol_address = ip, + }; + + // Send the request + arp_process_result_t res; + res = arp_process_packet(&state, &request, sizeof(arp_packet_t)); + switch (res) { + + case ARP_PROCESS_RESULT_INVALID: + break; + + case ARP_PROCESS_RESULT_HWARENOTSUPP: + case ARP_PROCESS_RESULT_PROTONOTSUPP: + snprintf(msg, msgmax, "ARP module couldn't process request"); + return TEST_FAILED; + + case ARP_PROCESS_RESULT_OK: + snprintf(msg, msgmax, "ARP module processed a request for an invalid protocol address length"); + return TEST_FAILED; + } + + if (oq.count > 0) { + // Sent replies + snprintf(msg, msgmax, "ARP module replied even though it failed to process the request"); + return TEST_FAILED; + } + + return TEST_PASSED; +} + +int main(void) +{ + void *tests[] = { + test_arp_bb_A, + test_arp_bb_B, + test_arp_bb_C, + test_arp_bb_D, + test_arp_bb_E, + }; + + char msg[256]; + for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + test_result_t (*test_fn)(char*, size_t) = tests[i]; + switch (test_fn(msg, sizeof(msg))) { + case TEST_PASSED: fprintf(stdout, "PASSED\n"); break; + case TEST_FAILED: fprintf(stdout, "FAILED: %s\n", msg); break; + case TEST_ABORTED: fprintf(stdout, "ABORTED: %s\n", msg); break; + } + } + return 0; +} \ No newline at end of file