rewrote the readme and cleaned up unused files

This commit is contained in:
cozis
2023-10-31 13:06:04 +01:00
parent 733597b6aa
commit 7033e1ea85
8 changed files with 12 additions and 715 deletions
+12 -17
View File
@@ -1,18 +1,13 @@
# MicroTCP # MicroTCP
Micro TCP is a network stack designed to be easily embeddable, portable and thoroughly tested. It implements ARP, IP, ICMP and TCP. The ideal use-cases are user-space networking and using it in bare-metal microcontrollers. MicroTCP is a TCP/IP network stack which I started building as a learning exercise while I was attending the Computer Networking course at the Università degli Studi di Napoli Federico II. It's just a hobby project and is intended to just be a minimal, yet complete, implementation.
At this moment MicroTCP implements ARP (RFC 826, complete), IPv4 (no fragmentation), ICMP (minimum necessary to reply to pings) and TCP (complete but not stress-tested). Note that "complete" should be not intended as "fully compliant" but just as a measure of progress on all of the major features. For instance, it's complete enough to handle HTTP traffic on a local network (Look into examples/microhttp to know more).
## Where does it run?
MicroTCP can run on Windows and Linux alongside the OS's network stack. To route the network traffic to MicroTCP, the process running it behaves as a virtual host with its own IP address. This is done using a TAP device, which comes built-in on Linux but needs installing on Windows. It should be very easy to adapt to run on microcontrollers but haven't tried yet, the dream is to serve my [blog](https://cozis.github.io/) from an STM32 board.
## Usage ## Usage
To use it, you need to instanciate a `microtcp_t` structure using either the `microtcp_create` or `microtcp_create_using_callbacks` constructor. MicroTCP's uses the usual socket interface any network programmer is familiar with, the main difference being you need to esplicitly instanciate the network stack and pass its handle around. Without further ado, here's a simple echo server that shows the basic usage:
The basic constructor `microtcp_create` behaves differently based on your platform because it needs to plug the stack to the system. At the moment it's assumed a Linux host and the instanciated stack is associated to a tap device `tap0` with IP `10.0.0.5/24`. The stack produces packets with IP `10.0.0.4/24` (being honest, this configuration is only useful for testing).
It's also possible to configure the stack explicitly using the `microtcp_create_using_callbacks`, which lets the caller provide the callbacks to input the ethernet frames to the stack and send frames back on the wire. Each system will need it's specific implementation of these callbacks.
Each instance of MicroTCP (without considering the callbacks) is completely isolated from the others, therefore, if your specific callback implementation allows it, you can have as many instances as you like! Usually the callbacks introduce a dependency between the stacks because the system is one big global state.
Once instanciated, you can free the stack using `microtcp_destroy`.
Once a `microtcp_t` instance is created, you can create and use sockets with methods analogous to the BSD socket API. For instance, here's a simple TCP echo server which replies to messages with the message itself prefixed with "echo: ":
```c ```c
#include <microtcp.h> #include <microtcp.h>
@@ -45,10 +40,10 @@ int main(void)
// If you want to use this code, you probably want to // If you want to use this code, you probably want to
// add some checks! // add some checks!
``` ```
This should be pretty straight forward to understand. One thing may be worth noting is that `microtcp_open` behaves as the BSD's `socket+bind+listen` all at once to setup a listening TCP server.
## Contributing There are more than one way to set up the stack, the main way being `microtcp_create` which creates a virtual network inferface on the host OS with IP 10.0.0.5/24 and a virtual host for the MicroTCP process with the 10.0.0.4/24 IP. You can open Wireshark on the virtual NIC to inspect the traffic between the host and the process.
The build result is a header and a .c obtained as an amalgamation of all the source files. Any header included in the source files must be guarded by a `#ifndef MICROTCP_AMALGAMATION`.
## Functionalities It's also possible to configure the stack using the `microtcp_create_using_callbacks`, which lets you explicitly provide the input L2 frames to it and receive the frames in a buffer. This is how one would run the stack on a microcontroller.
MicroTCP will not support
* TCP options Each instance of MicroTCP (without considering the callbacks) is completely isolated from the others, therefore, if your specific callback implementation allows it, you can have as many instances as you like! Usually the callbacks introduce a dependency between the stacks because the system is one big global state.
-25
View File
@@ -1,25 +0,0 @@
int main(void)
{
microtcp_t *mtcp = microtcp_create(..);
uint16_t port = 8080;
microtcp_socket_t *socket = microtcp_open(mtcp, port, 0);
microtcp_mux_t *mux = microtcp_mux_create(mtcp);
for (int i = 0; i < 3; i++) {
microtcp_socket_t *accepted = microtcp_accept(socket, 0, 0);
microtcp_mux_register(mux, accepted, MICROTCP_MUX_RECV | MICROTCP_MUX_SEND);
}
for (microtcp_muxevent_t event; microtcp_mux_wait(mux, &event)) {
if (event.events & MICROTCP_MUX_RECV) {
// Il socket "event.socket" ha dei dati da leggere
} else {
// Il socket "event.socket" ha spazio per inviare
}
}
microtcp_destroy(mtcp);
}
-10
View File
@@ -143,15 +143,5 @@ build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/*
build/echo_http: examples/microhttp/main.c $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.h build/microtcp.c build/echo_http: examples/microhttp/main.c $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.h build/microtcp.c
gcc examples/microhttp/main.c examples/microhttp/xhttp.c build/microtcp.c -o $@ $(CFLAGS) $(LFLAGS) -DDEBUG=1 -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_USING_MUX gcc examples/microhttp/main.c examples/microhttp/xhttp.c build/microtcp.c -o $@ $(CFLAGS) $(LFLAGS) -DDEBUG=1 -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_USING_MUX
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
clean: clean:
rm -fr build 3p/libtuntap/build 3p/lib/* 3p/include/* rm -fr build 3p/libtuntap/build 3p/lib/* 3p/include/*
-15
View File
@@ -1,15 +0,0 @@
from http.server import BaseHTTPRequestHandler, HTTPServer
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
message = "Hello world!"
self.wfile.write(bytes(message, "utf8"))
return
addr = ('127.0.0.1', 8080)
HTTPServer(addr, RequestHandler).serve_forever()
-11
View File
@@ -1,11 +0,0 @@
@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 ]
-8
View File
@@ -1,8 +0,0 @@
void test_tcp_timer(void);
int main(void)
{
test_tcp_timer();
return 0;
}
-463
View File
@@ -1,463 +0,0 @@
#include <stdio.h>
#include <string.h>
#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;
}
-166
View File
@@ -1,166 +0,0 @@
#include <assert.h>
#include "../src/tcp_timer.h"
static void set_int_variable_to_1(void *data)
{
int *n = data;
*n = 1;
}
static void test_timer(void)
{
static_assert(TCP_MAX_TIMERS > 0);
tcp_timerset_t set;
tcp_timerset_init(&set);
int resolved = 0;
tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved);
assert(t1);
assert(resolved == 0);
tcp_timerset_step(&set, 1);
assert(resolved == 0);
tcp_timerset_step(&set, 4);
assert(resolved == 1);
tcp_timerset_free(&set);
}
static void test_disabled_timer(void)
{
static_assert(TCP_MAX_TIMERS > 0);
tcp_timerset_t set;
tcp_timerset_init(&set);
int resolved = 0;
tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved);
assert(t1);
assert(resolved == 0);
tcp_timer_disable(t1);
tcp_timerset_step(&set, 1);
assert(resolved == 0);
tcp_timerset_step(&set, 4);
assert(resolved == 0);
tcp_timerset_free(&set);
}
static void test_disabled_timer_2(void)
{
static_assert(TCP_MAX_TIMERS > 0);
tcp_timerset_t set;
tcp_timerset_init(&set);
int resolved = 0;
tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved);
assert(t1);
assert(resolved == 0);
tcp_timerset_step(&set, 1);
assert(resolved == 0);
tcp_timer_disable(t1);
tcp_timerset_step(&set, 4);
assert(resolved == 0);
tcp_timerset_free(&set);
}
static void test_disabled_timer_3(void)
{
static_assert(TCP_MAX_TIMERS > 0);
tcp_timerset_t set;
tcp_timerset_init(&set);
int resolved_1 = 0;
int resolved_2 = 0;
tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved_1);
tcp_timer_t *t2 = tcp_timer_create(&set, 5, "t2", set_int_variable_to_1, &resolved_2);
assert(t1);
assert(t2);
assert(resolved_1 == 0);
assert(resolved_2 == 0);
tcp_timerset_step(&set, 1);
assert(resolved_1 == 0);
assert(resolved_2 == 0);
tcp_timer_disable(t1);
tcp_timerset_step(&set, 4);
assert(resolved_1 == 0);
assert(resolved_2 == 1);
tcp_timerset_free(&set);
}
static void test_disabled_timer_4(void)
{
static_assert(TCP_MAX_TIMERS > 0);
tcp_timerset_t set;
tcp_timerset_init(&set);
int resolved_1 = 0;
int resolved_2 = 0;
tcp_timer_t *t1 = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &resolved_1);
tcp_timer_t *t2 = tcp_timer_create(&set, 5, "t2", set_int_variable_to_1, &resolved_2);
assert(t1);
assert(t2);
assert(resolved_1 == 0);
assert(resolved_2 == 0);
tcp_timerset_step(&set, 1);
assert(resolved_1 == 0);
assert(resolved_2 == 0);
tcp_timer_disable(t2);
tcp_timerset_step(&set, 4);
assert(resolved_1 == 1);
assert(resolved_2 == 0);
tcp_timerset_free(&set);
}
static void test_out_of_timers(void)
{
static_assert(TCP_MAX_TIMERS > 0);
tcp_timerset_t set;
tcp_timerset_init(&set);
int temp;
for (int i = 0; i < TCP_MAX_TIMERS; i++) {
tcp_timer_t *t = tcp_timer_create(&set, 5, "t1", set_int_variable_to_1, &temp);
assert(t != NULL);
}
tcp_timer_t *t = tcp_timer_create(&set, 5, "t2", set_int_variable_to_1, &temp);
assert(t == NULL);
tcp_timerset_free(&set);
}
void test_tcp_timer(void)
{
test_timer();
test_disabled_timer();
test_disabled_timer_2();
test_disabled_timer_3();
test_disabled_timer_4();
test_out_of_timers();
}